1 /////////////////////////////////////////////////////////////////////////////
2 // WinMerge: an interactive diff/merge utility
3 // Copyright (C) 1997-2000 Thingamahoochie Software
5 // SPDX-License-Identifier: GPL-2.0-or-later
6 /////////////////////////////////////////////////////////////////////////////
8 * @file HexMergeDoc.cpp
10 * @brief Implementation file for CHexMergeDoc
15 #include "HexMergeDoc.h"
17 #include "UnicodeString.h"
18 #include "HexMergeFrm.h"
19 #include "HexMergeView.h"
21 #include "FolderCmp.h"
22 #include "DiffContext.h" // FILE_SAME
24 #include "DirActions.h"
25 #include "OptionsDef.h"
26 #include "DiffFileInfo.h"
27 #include "SaveClosingDlg.h"
28 #include "SelectPluginDlg.h"
31 #include "OptionsMgr.h"
32 #include "FileOrFolderSelect.h"
33 #include "DiffWrapper.h"
34 #include "SyntaxColors.h"
36 #include "Constants.h"
43 int CHexMergeDoc::m_nBuffersTemp = 2;
45 static void UpdateDiffItem(int nBuffers, DIFFITEM &di, CDiffContext *pCtxt);
46 static int Try(HRESULT hr, UINT type = MB_OKCANCEL|MB_ICONSTOP);
49 * @brief Update diff item
51 static void UpdateDiffItem(int nBuffers, DIFFITEM &di, CDiffContext *pCtxt)
53 di.diffcode.setSideNone();
54 for (int nBuffer = 0; nBuffer < nBuffers; nBuffer++)
56 di.diffFileInfo[nBuffer].ClearPartial();
57 if (pCtxt->UpdateInfoFromDiskHalf(di, nBuffer))
58 di.diffcode.diffcode |= DIFFCODE::FIRST << nBuffer;
61 di.diffcode.diffcode &= ~(DIFFCODE::TEXTFLAGS | DIFFCODE::COMPAREFLAGS | DIFFCODE::COMPAREFLAGS3WAY);
63 FolderCmp folderCmp(pCtxt);
64 di.diffcode.diffcode |= folderCmp.prepAndCompareFiles(di);
68 * @brief Issue an error popup if passed in HRESULT is nonzero
70 static int Try(HRESULT hr, UINT type)
72 return hr ? CInternetException(hr).ReportError(type) : 0;
75 /////////////////////////////////////////////////////////////////////////////
78 IMPLEMENT_DYNCREATE(CHexMergeDoc, CDocument)
80 BEGIN_MESSAGE_MAP(CHexMergeDoc, CDocument)
81 //{{AFX_MSG_MAP(CHexMergeDoc)
83 ON_COMMAND(ID_FILE_SAVE, OnFileSave)
84 ON_COMMAND(ID_FILE_SAVE_LEFT, OnFileSaveLeft)
85 ON_COMMAND(ID_FILE_SAVE_MIDDLE, OnFileSaveMiddle)
86 ON_COMMAND(ID_FILE_SAVE_RIGHT, OnFileSaveRight)
87 ON_COMMAND(ID_FILE_SAVEAS_LEFT, OnFileSaveAsLeft)
88 ON_COMMAND(ID_FILE_SAVEAS_MIDDLE, OnFileSaveAsMiddle)
89 ON_COMMAND(ID_FILE_SAVEAS_RIGHT, OnFileSaveAsRight)
90 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
91 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_LEFT, OnUpdateFileSaveLeft)
92 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_MIDDLE, OnUpdateFileSaveMiddle)
93 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_RIGHT, OnUpdateFileSaveRight)
94 ON_COMMAND(ID_RESCAN, OnFileReload)
95 ON_COMMAND_RANGE(ID_MERGE_COMPARE_TEXT, ID_MERGE_COMPARE_WEBPAGE, OnFileRecompareAs)
96 ON_UPDATE_COMMAND_UI_RANGE(ID_MERGE_COMPARE_TEXT, ID_MERGE_COMPARE_WEBPAGE, OnUpdateFileRecompareAs)
98 ON_COMMAND(ID_VIEW_ZOOMIN, OnViewZoomIn)
99 ON_COMMAND(ID_VIEW_ZOOMOUT, OnViewZoomOut)
100 ON_COMMAND(ID_VIEW_ZOOMNORMAL, OnViewZoomNormal)
101 ON_COMMAND(ID_REFRESH, OnRefresh)
103 ON_COMMAND(ID_L2R, OnL2r)
104 ON_COMMAND(ID_R2L, OnR2l)
105 ON_UPDATE_COMMAND_UI(ID_L2R, OnUpdateL2r)
106 ON_UPDATE_COMMAND_UI(ID_R2L, OnUpdateR2l)
107 ON_UPDATE_COMMAND_UI(ID_L2RNEXT, OnUpdateL2RNext)
108 ON_UPDATE_COMMAND_UI(ID_R2LNEXT, OnUpdateR2LNext)
109 ON_COMMAND(ID_COPY_FROM_LEFT, OnCopyFromLeft)
110 ON_COMMAND(ID_COPY_FROM_RIGHT, OnCopyFromRight)
111 ON_UPDATE_COMMAND_UI(ID_COPY_FROM_LEFT, OnUpdateCopyFromLeft)
112 ON_UPDATE_COMMAND_UI(ID_COPY_FROM_RIGHT, OnUpdateCopyFromRight)
113 ON_COMMAND(ID_ALL_LEFT, OnAllLeft)
114 ON_COMMAND(ID_ALL_RIGHT, OnAllRight)
115 ON_UPDATE_COMMAND_UI(ID_ALL_LEFT, OnUpdateAllLeft)
116 ON_UPDATE_COMMAND_UI(ID_ALL_RIGHT, OnUpdateAllRight)
117 ON_COMMAND_RANGE(ID_COPY_TO_MIDDLE_L, ID_COPY_FROM_LEFT_R, OnCopyX2Y)
118 ON_UPDATE_COMMAND_UI_RANGE(ID_COPY_TO_MIDDLE_L, ID_COPY_FROM_LEFT_R, OnUpdateX2Y)
120 ON_COMMAND_RANGE(ID_UNPACKERS_FIRST, ID_UNPACKERS_LAST, OnFileRecompareAs)
121 ON_COMMAND(ID_OPEN_WITH_UNPACKER, OnOpenWithUnpacker)
123 ON_UPDATE_COMMAND_UI(ID_STATUS_DIFFNUM, OnUpdateStatusNum)
127 /////////////////////////////////////////////////////////////////////////////
128 // CHexMergeDoc construction/destruction
131 * @brief Constructor.
133 CHexMergeDoc::CHexMergeDoc()
135 , m_nBuffers(m_nBuffersTemp)
137 , m_nBufferType{BUFFERTYPE::NORMAL, BUFFERTYPE::NORMAL, BUFFERTYPE::NORMAL}
139 m_filePaths.SetSize(m_nBuffers);
145 * Informs associated dirdoc that mergedoc is closing.
147 CHexMergeDoc::~CHexMergeDoc()
149 GetMainFrame()->UnwatchDocuments(this);
151 if (m_pDirDoc != nullptr)
152 m_pDirDoc->MergeDocClosing(this);
156 * @brief Return active merge edit view (or left one if neither active)
158 CHexMergeView * CHexMergeDoc::GetActiveMergeView() const
160 CView * pActiveView = GetParentFrame()->GetActiveView();
161 CHexMergeView * pHexMergeView = dynamic_cast<CHexMergeView *>(pActiveView);
162 if (pHexMergeView == nullptr)
163 pHexMergeView = m_pView[0]; // default to left view (in case some location or detail view active)
164 return pHexMergeView;
168 * @brief Update associated diff item
170 int CHexMergeDoc::UpdateDiffItem(CDirDoc *pDirDoc)
172 // If directory compare has results
173 if (pDirDoc != nullptr && pDirDoc->HasDiffs())
175 CDiffContext &ctxt = pDirDoc->GetDiffContext();
176 if (DIFFITEM *pos = FindItemFromPaths(ctxt, m_filePaths))
178 DIFFITEM &di = ctxt.GetDiffRefAt(pos);
179 ::UpdateDiffItem(m_nBuffers, di, &ctxt);
183 size_t lengthFirst = m_pView[0]->GetLength();
184 void *bufferFirst = m_pView[0]->GetBuffer(lengthFirst);
185 for (int nBuffer = 1; nBuffer < m_nBuffers; nBuffer++)
187 size_t length = m_pView[nBuffer]->GetLength();
188 if (lengthFirst != length)
192 void *buffer = m_pView[nBuffer]->GetBuffer(length);
193 bDiff = (memcmp(bufferFirst, buffer, lengthFirst) != 0);
198 GetParentFrame()->SetLastCompareResult(bDiff);
199 return bDiff ? 1 : 0;
203 * @brief Asks and then saves modified files
205 bool CHexMergeDoc::PromptAndSaveIfNeeded(bool bAllowCancel)
207 bool bLModified = false, bMModified = false, bRModified = false;
211 bLModified = m_pView[0]->GetModified();
212 bMModified = m_pView[1]->GetModified();
213 bRModified = m_pView[2]->GetModified();
217 bLModified = m_pView[0]->GetModified();
218 bRModified = m_pView[1]->GetModified();
220 if (!bLModified && !bMModified && !bRModified)
223 const String &pathLeft = m_filePaths.GetLeft();
224 const String &pathMiddle = m_filePaths.GetMiddle();
225 const String &pathRight = m_filePaths.GetRight();
228 bool bLSaveSuccess = false, bMSaveSuccess = false, bRSaveSuccess = false;
231 dlg.DoAskFor(bLModified, bMModified, bRModified);
233 dlg.m_bDisableCancel = true;
234 if (!pathLeft.empty())
235 dlg.m_sLeftFile = pathLeft;
237 dlg.m_sLeftFile = m_strDesc[0];
240 if (!pathMiddle.empty())
241 dlg.m_sMiddleFile = pathMiddle;
243 dlg.m_sMiddleFile = m_strDesc[1];
245 if (!pathRight.empty())
246 dlg.m_sRightFile = pathRight;
248 dlg.m_sRightFile = m_strDesc[1];
250 if (dlg.DoModal() == IDOK)
254 if (dlg.m_leftSave == SaveClosingDlg::SAVECLOSING_SAVE)
256 bLSaveSuccess = DoFileSave(0);
262 m_pView[0]->SetSavePoint();
267 if (dlg.m_middleSave == SaveClosingDlg::SAVECLOSING_SAVE)
269 bMSaveSuccess = DoFileSave(1);
275 m_pView[1]->SetSavePoint();
280 if (dlg.m_rightSave == SaveClosingDlg::SAVECLOSING_SAVE)
282 bRSaveSuccess = DoFileSave(m_nBuffers - 1);
288 m_pView[m_nBuffers - 1]->SetSavePoint();
297 // If file were modified and saving was successfull,
298 // update status on dir view
299 if (bLSaveSuccess || bMSaveSuccess || bRSaveSuccess)
301 UpdateDiffItem(m_pDirDoc);
308 * @brief Save modified documents
310 BOOL CHexMergeDoc::SaveModified()
312 return PromptAndSaveIfNeeded(true);
316 * @brief Saves both files
318 void CHexMergeDoc::OnFileSave()
320 for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
324 bool CHexMergeDoc::DoFileSave(int nBuffer)
327 if (m_pView[nBuffer]->GetModified())
329 if (m_nBufferType[nBuffer] == BUFFERTYPE::UNNAMED)
330 result = DoFileSaveAs(nBuffer);
333 const String &path = m_filePaths.GetPath(nBuffer);
334 HRESULT hr = m_pView[nBuffer]->SaveFile(path.c_str());
335 if (Try(hr) == IDCANCEL)
338 return DoFileSaveAs(nBuffer);
341 UpdateDiffItem(m_pDirDoc);
347 bool CHexMergeDoc::DoFileSaveAs(int nBuffer, bool packing)
349 const String &path = m_filePaths.GetPath(nBuffer);
353 title = _("Save Left File As");
354 else if (nBuffer == m_nBuffers - 1)
355 title = _("Save Right File As");
357 title = _("Save Middle File As");
358 if (SelectFile(AfxGetMainWnd()->GetSafeHwnd(), strPath, false, path.c_str(), title))
360 HRESULT hr = m_pView[nBuffer]->SaveFile(strPath.c_str());
361 if (Try(hr) == IDCANCEL)
367 // We are saving scratchpad (unnamed file)
368 m_nBufferType[nBuffer] = BUFFERTYPE::UNNAMED_SAVED;
369 m_strDesc[nBuffer].erase();
372 m_filePaths.SetPath(nBuffer, strPath);
373 UpdateDiffItem(m_pDirDoc);
374 UpdateHeaderPath(nBuffer);
381 * @brief Saves left-side file
383 void CHexMergeDoc::OnFileSaveLeft()
389 * @brief Saves middle-side file
391 void CHexMergeDoc::OnFileSaveMiddle()
397 * @brief Saves right-side file
399 void CHexMergeDoc::OnFileSaveRight()
401 DoFileSave(m_nBuffers - 1);
405 * @brief Saves left-side file with name asked
407 void CHexMergeDoc::OnFileSaveAsLeft()
413 * @brief Saves right-side file with name asked
415 void CHexMergeDoc::OnFileSaveAsMiddle()
421 * @brief Saves right-side file with name asked
423 void CHexMergeDoc::OnFileSaveAsRight()
425 DoFileSaveAs(m_nBuffers - 1);
429 * @brief Update diff-number pane text
431 void CHexMergeDoc::OnUpdateStatusNum(CCmdUI* pCmdUI)
434 pCmdUI->SetText(s.c_str());
438 * @brief DirDoc gives us its identity just after it creates us
440 void CHexMergeDoc::SetDirDoc(CDirDoc * pDirDoc)
442 ASSERT(pDirDoc != nullptr && m_pDirDoc == nullptr);
446 bool CHexMergeDoc::GetReadOnly(int nBuffer) const
448 return m_pView[nBuffer]->GetReadOnly();
452 * @brief Return pointer to parent frame
454 CHexMergeFrame * CHexMergeDoc::GetParentFrame() const
456 return static_cast<CHexMergeFrame *>(m_pView[0]->GetParentFrame());
460 * @brief DirDoc is closing
462 void CHexMergeDoc::DirDocClosing(CDirDoc * pDirDoc)
464 ASSERT(m_pDirDoc == pDirDoc);
469 * @brief DirDoc commanding us to close
471 bool CHexMergeDoc::CloseNow()
473 // Allow user to cancel closing
474 if (!PromptAndSaveIfNeeded(true))
477 GetParentFrame()->DestroyWindow();
482 * @brief A string to display as a tooltip for MDITabbar
484 CString CHexMergeDoc::GetTooltipString() const
486 return CMergeFrameCommon::GetTooltipString(m_filePaths, m_strDesc, &m_infoUnpacker, nullptr).c_str();
490 * @brief Load one file
492 HRESULT CHexMergeDoc::LoadOneFile(int index, LPCTSTR filename, bool readOnly, const String& strDesc)
496 if (Try(m_pView[index]->LoadFile(filename), MB_ICONSTOP) != 0)
498 m_pView[index]->SetReadOnly(readOnly);
499 m_filePaths.SetPath(index, filename, false);
500 ASSERT(m_nBufferType[index] == BUFFERTYPE::NORMAL); // should have been initialized to BUFFERTYPE::NORMAL in constructor
501 if (!strDesc.empty())
503 m_strDesc[index] = strDesc;
504 m_nBufferType[index] = BUFFERTYPE::NORMAL_NAMED;
509 m_nBufferType[index] = BUFFERTYPE::UNNAMED;
510 m_strDesc[index] = strDesc;
512 UpdateHeaderPath(index);
513 m_pView[index]->ResizeWindow();
518 * @brief Load files and initialize frame's compare result icon
520 bool CHexMergeDoc::OpenDocs(int nFiles, const FileLocation fileloc[], const bool bRO[], const String strDesc[])
522 CWaitCursor waitstatus;
523 CHexMergeFrame *pf = GetParentFrame();
524 ASSERT(pf != nullptr);
525 bool bSucceeded = true;
526 int nNormalBuffer = 0;
528 for (nBuffer = 0; nBuffer < nFiles; nBuffer++)
530 if (FAILED(LoadOneFile(nBuffer, fileloc[nBuffer].filepath.c_str(), bRO[nBuffer], strDesc ? strDesc[nBuffer] : _T(""))))
535 if (m_nBufferType[nBuffer] == BUFFERTYPE::NORMAL || m_nBufferType[nBuffer] == BUFFERTYPE::NORMAL_NAMED)
538 if (std::count(m_nBufferType, m_nBufferType + m_nBuffers, BUFFERTYPE::UNNAMED) == m_nBuffers)
539 m_infoUnpacker.Initialize(false);
540 if (nBuffer == nFiles)
542 // An extra ResizeWindow() on the left view aligns scroll ranges, and
543 // also triggers initial diff coloring by invalidating the client area.
544 m_pView[0]->ResizeWindow();
546 if (nNormalBuffer > 0)
549 UpdateDiffItem(m_pDirDoc);
553 // Use verify macro to trap possible error in debug.
554 VERIFY(pf->DestroyWindow());
557 GetMainFrame()->WatchDocuments(this);
562 void CHexMergeDoc::MoveOnLoad(int nPane, int)
566 nPane = (m_nBufferType[0] != BUFFERTYPE::UNNAMED) ? GetOptionsMgr()->GetInt(OPT_ACTIVE_PANE) : 0;
567 if (nPane < 0 || nPane >= m_nBuffers)
571 GetParentFrame()->SetActivePane(nPane);
573 if (GetOptionsMgr()->GetBool(OPT_SCROLL_TO_FIRST))
574 m_pView[0]->SendMessage(WM_COMMAND, ID_FIRSTDIFF);
577 void CHexMergeDoc::ChangeFile(int nBuffer, const String& path, int nLineIndex)
579 if (!PromptAndSaveIfNeeded(true))
581 m_nBufferType[nBuffer] = BUFFERTYPE::NORMAL;
582 m_strDesc[nBuffer].clear();
583 m_pView[nBuffer]->ClearUndoRecords();
584 LoadOneFile(nBuffer, path.c_str(), m_pView[nBuffer]->GetReadOnly(), _T(""));
587 void CHexMergeDoc::CheckFileChanged(void)
589 for (int pane = 0; pane < m_nBuffers; ++pane)
591 if (m_pView[pane]->IsFileChangedOnDisk(m_filePaths[pane].c_str()) == FileChange::Changed)
593 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]);
594 if (AfxMessageBox(msg.c_str(), MB_YESNO | MB_ICONWARNING | MB_DONT_ASK_AGAIN, IDS_FILECHANGED_RESCAN) == IDYES)
604 * @brief Write path and filename to headerbar
605 * @note SetText() does not repaint unchanged text
607 void CHexMergeDoc::UpdateHeaderPath(int pane)
609 CHexMergeFrame *pf = GetParentFrame();
610 ASSERT(pf != nullptr);
613 if (m_nBufferType[pane] == BUFFERTYPE::UNNAMED ||
614 m_nBufferType[pane] == BUFFERTYPE::NORMAL_NAMED)
616 sText = m_strDesc[pane];
620 sText = m_filePaths.GetPath(pane);
621 if (m_pDirDoc != nullptr)
622 m_pDirDoc->ApplyDisplayRoot(pane, sText);
624 if (m_pView[pane]->GetModified())
625 sText.insert(0, _T("* "));
626 pf->GetHeaderInterface()->SetText(pane, sText);
633 * @brief Customize a heksedit control's settings
635 static void Customize(IHexEditorWindow::Settings *settings)
637 settings->bSaveIni = false;
638 settings->iFontZoom = static_cast<int>(settings->iFontSize * GetOptionsMgr()->GetInt(OPT_VIEW_ZOOM) / 1000.0 - settings->iFontSize);
639 //settings->iAutomaticBPL = false;
640 //settings->iBytesPerLine = 16;
641 //settings->iFontSize = 8;
645 * @brief Customize a heksedit control's colors
647 static void Customize(IHexEditorWindow::Colors *colors)
649 COptionsMgr *pOptionsMgr = GetOptionsMgr();
650 colors->iSelBkColorValue = RGB(224, 224, 224);
651 colors->iDiffBkColorValue = pOptionsMgr->GetInt(OPT_CLR_DIFF);
652 colors->iSelDiffBkColorValue = pOptionsMgr->GetInt(OPT_CLR_SELECTED_DIFF);
653 colors->iDiffTextColorValue = pOptionsMgr->GetInt(OPT_CLR_DIFF_TEXT);
654 if (colors->iDiffTextColorValue == 0xFFFFFFFF)
655 colors->iDiffTextColorValue = 0;
656 colors->iSelDiffTextColorValue = pOptionsMgr->GetInt(OPT_CLR_SELECTED_DIFF_TEXT);
657 if (colors->iSelDiffTextColorValue == 0xFFFFFFFF)
658 colors->iSelDiffTextColorValue = 0;
659 SyntaxColors *pSyntaxColors = theApp.GetMainSyntaxColors();
660 colors->iTextColorValue = pSyntaxColors->GetColor(COLORINDEX_NORMALTEXT);
661 colors->iBkColorValue = pSyntaxColors->GetColor(COLORINDEX_BKGND);
662 colors->iSelTextColorValue = pSyntaxColors->GetColor(COLORINDEX_SELTEXT);
663 colors->iSelBkColorValue = pSyntaxColors->GetColor(COLORINDEX_SELBKGND);
667 * @brief Customize a heksedit control's settings and colors
669 static void Customize(IHexEditorWindow *pif)
671 Customize(pif->get_settings());
672 Customize(pif->get_colors());
673 //LANGID wLangID = (LANGID)GetThreadLocale();
674 //pif->load_lang(wLangID);
677 void CHexMergeDoc::RefreshOptions()
679 for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
681 IHexEditorWindow *pif = m_pView[nBuffer]->GetInterface();
682 pif->read_ini_data();
684 pif->resize_window();
689 * @brief Update document filenames to title
691 void CHexMergeDoc::SetTitle(LPCTSTR lpszTitle)
693 String sTitle = (lpszTitle != nullptr) ? lpszTitle : CMergeFrameCommon::GetTitleString(m_filePaths, m_strDesc, &m_infoUnpacker, nullptr);
694 CDocument::SetTitle(sTitle.c_str());
695 if (auto* pParentFrame = GetParentFrame())
696 pParentFrame->SetWindowText(sTitle.c_str());
700 * @brief We have two child views (left & right), so we keep pointers directly
701 * at them (the MFC view list doesn't have them both)
703 void CHexMergeDoc::SetMergeViews(CHexMergeView *pView[])
705 for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
707 ASSERT(pView[nBuffer] != nullptr && m_pView[nBuffer] == nullptr);
708 m_pView[nBuffer] = pView[nBuffer];
709 m_pView[nBuffer]->m_nThisPane = nBuffer;
714 * @brief Called when "Save left" item is updated
716 void CHexMergeDoc::OnUpdateFileSaveLeft(CCmdUI* pCmdUI)
718 pCmdUI->Enable(m_pView[0]->GetModified());
722 * @brief Called when "Save middle" item is updated
724 void CHexMergeDoc::OnUpdateFileSaveMiddle(CCmdUI* pCmdUI)
726 pCmdUI->Enable(m_nBuffers == 3 && m_pView[1]->GetModified());
730 * @brief Called when "Save right" item is updated
732 void CHexMergeDoc::OnUpdateFileSaveRight(CCmdUI* pCmdUI)
734 pCmdUI->Enable(m_pView[m_nBuffers - 1]->GetModified());
738 * @brief Called when "Save" item is updated
740 void CHexMergeDoc::OnUpdateFileSave(CCmdUI* pCmdUI)
742 bool bModified = false;
743 for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
744 bModified |= m_pView[nBuffer]->GetModified();
745 pCmdUI->Enable(bModified);
749 * @brief Reloads the opened files
751 void CHexMergeDoc::OnFileReload()
753 if (!PromptAndSaveIfNeeded(true))
756 FileLocation fileloc[3];
758 for (int pane = 0; pane < m_nBuffers; pane++)
760 fileloc[pane].setPath(m_filePaths[pane]);
761 bRO[pane] = m_pView[pane]->GetReadOnly();
763 int nActivePane = GetActiveMergeView()->m_nThisPane;
764 if (!OpenDocs(m_nBuffers, fileloc, bRO, m_strDesc))
766 MoveOnLoad(nActivePane);
769 void CHexMergeDoc::OnCopyX2Y(UINT nID)
771 auto [srcPane, dstPane] = CMergeFrameCommon::MenuIDtoXY(nID, GetActiveMergeView()->m_nThisPane, m_nBuffers);
772 if (srcPane >= 0 && dstPane >= 0)
773 CHexMergeView::CopySel(m_pView[srcPane], m_pView[dstPane]);
776 void CHexMergeDoc::OnUpdateX2Y(CCmdUI* pCmdUI)
778 auto [srcPane, dstPane] = CMergeFrameCommon::MenuIDtoXY(pCmdUI->m_nID, GetActiveMergeView()->m_nThisPane, m_nBuffers);
779 if (srcPane < 0 || dstPane < 0)
780 pCmdUI->Enable(false);
782 pCmdUI->Enable(!GetReadOnly(dstPane));
784 CMergeFrameCommon::ChangeMergeMenuText(srcPane, dstPane, pCmdUI);
788 * @brief Copy selected bytes from left to right
790 void CHexMergeDoc::OnL2r()
796 * @brief Called when "Copy to Right" item is updated
798 void CHexMergeDoc::OnUpdateL2r(CCmdUI* pCmdUI)
804 * @brief Copy selected bytes from right to left
806 void CHexMergeDoc::OnR2l()
812 * @brief Called when "Copy to Left" item is updated
814 void CHexMergeDoc::OnUpdateR2l(CCmdUI* pCmdUI)
820 * @brief Update "Copy right and advance" UI item
822 void CHexMergeDoc::OnUpdateL2RNext(CCmdUI* pCmdUI)
824 auto [srcPane, dstPane] = CMergeFrameCommon::MenuIDtoXY(ID_L2RNEXT, GetActiveMergeView()->m_nThisPane, m_nBuffers);
825 pCmdUI->Enable(false);
826 CMergeFrameCommon::ChangeMergeMenuText(srcPane, dstPane, pCmdUI);
830 * @brief Update "Copy left and advance" UI item
832 void CHexMergeDoc::OnUpdateR2LNext(CCmdUI* pCmdUI)
834 auto [srcPane, dstPane] = CMergeFrameCommon::MenuIDtoXY(ID_R2LNEXT, GetActiveMergeView()->m_nThisPane, m_nBuffers);
835 pCmdUI->Enable(false);
836 CMergeFrameCommon::ChangeMergeMenuText(srcPane, dstPane, pCmdUI);
840 * @brief Copy selected bytes from left to active pane
842 void CHexMergeDoc::OnCopyFromLeft()
844 OnCopyX2Y(ID_COPY_FROM_LEFT);
848 * @brief Called when "Copy from left" item is updated
850 void CHexMergeDoc::OnUpdateCopyFromLeft(CCmdUI* pCmdUI)
856 * @brief Copy selected bytes from right to active pane
858 void CHexMergeDoc::OnCopyFromRight()
860 OnCopyX2Y(ID_COPY_FROM_RIGHT);
864 * @brief Called when "Copy from right" item is updated
866 void CHexMergeDoc::OnUpdateCopyFromRight(CCmdUI* pCmdUI)
872 * @brief Copy all bytes from left to right
874 void CHexMergeDoc::OnAllRight()
876 auto [srcPane, dstPane] = CMergeFrameCommon::MenuIDtoXY(ID_ALL_RIGHT, GetActiveMergeView()->m_nThisPane, m_nBuffers);
877 CHexMergeView::CopyAll(m_pView[srcPane], m_pView[dstPane]);
881 * @brief Called when "Copy all to right" item is updated
883 void CHexMergeDoc::OnUpdateAllRight(CCmdUI* pCmdUI)
885 auto [srcPane, dstPane] = CMergeFrameCommon::MenuIDtoXY(pCmdUI->m_nID, GetActiveMergeView()->m_nThisPane, m_nBuffers);
886 pCmdUI->Enable(!GetReadOnly(dstPane));
888 CMergeFrameCommon::ChangeMergeMenuText(srcPane, dstPane, pCmdUI);
892 * @brief Copy all bytes from right to left
894 void CHexMergeDoc::OnAllLeft()
896 auto [srcPane, dstPane] = CMergeFrameCommon::MenuIDtoXY(ID_ALL_LEFT, GetActiveMergeView()->m_nThisPane, m_nBuffers);
897 CHexMergeView::CopyAll(m_pView[srcPane], m_pView[dstPane]);
901 * @brief Called when "Copy all to left" item is updated
903 void CHexMergeDoc::OnUpdateAllLeft(CCmdUI* pCmdUI)
905 auto [srcPane, dstPane] = CMergeFrameCommon::MenuIDtoXY(pCmdUI->m_nID, GetActiveMergeView()->m_nThisPane, m_nBuffers);
906 pCmdUI->Enable(!GetReadOnly(dstPane));
908 CMergeFrameCommon::ChangeMergeMenuText(srcPane, dstPane, pCmdUI);
912 * @brief Called when user selects View/Zoom In from menu.
914 void CHexMergeDoc::OnViewZoomIn()
916 for (int pane = 0; pane < m_nBuffers; pane++)
917 m_pView[pane]->ZoomText(1);
921 * @brief Called when user selects View/Zoom Out from menu.
923 void CHexMergeDoc::OnViewZoomOut()
925 for (int pane = 0; pane < m_nBuffers; pane++)
926 m_pView[pane]->ZoomText(-1);
930 * @brief Called when user selects View/Zoom Normal from menu.
932 void CHexMergeDoc::OnViewZoomNormal()
934 for (int pane = 0; pane < m_nBuffers; pane++)
935 m_pView[pane]->ZoomText(0);
938 void CHexMergeDoc::OnRefresh()
940 if (UpdateDiffItem(m_pDirDoc) == 0)
942 CMergeFrameCommon::ShowIdenticalMessage(m_filePaths, true,
943 [](LPCTSTR msg, UINT flags, UINT id) -> int { return AfxMessageBox(msg, flags, id); });
947 void CHexMergeDoc::OnFileRecompareAs(UINT nID)
949 PathContext paths = m_filePaths;
952 int nBuffers = m_nBuffers;
953 PackingInfo infoUnpacker(m_infoUnpacker.GetPluginPipeline());
955 for (int nBuffer = 0; nBuffer < nBuffers; ++nBuffer)
957 dwFlags[nBuffer] = m_pView[nBuffer]->GetReadOnly() ? FFILEOPEN_READONLY : 0;
958 strDesc[nBuffer] = m_strDesc[nBuffer];
960 if (ID_UNPACKERS_FIRST <= nID && nID <= ID_UNPACKERS_LAST)
962 infoUnpacker.SetPluginPipeline(CMainFrame::GetPluginPipelineByMenuId(nID, FileTransform::UnpackerEventNames, ID_UNPACKERS_FIRST));
963 nID = GetOptionsMgr()->GetBool(OPT_PLUGINS_OPEN_IN_SAME_FRAME_TYPE) ? ID_MERGE_COMPARE_HEX : -ID_MERGE_COMPARE_HEX;
967 GetMainFrame()->DoFileOrFolderOpen(&paths, dwFlags, strDesc, _T(""),
968 GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS), nullptr, &infoUnpacker, nullptr, nID);
971 void CHexMergeDoc::OnOpenWithUnpacker()
973 CSelectPluginDlg dlg(m_infoUnpacker.GetPluginPipeline(),
974 strutils::join(m_filePaths.begin(), m_filePaths.end(), _T("|")), CSelectPluginDlg::PluginType::Unpacker, false);
975 if (dlg.DoModal() == IDOK)
977 PackingInfo infoUnpacker(dlg.GetPluginPipeline());
978 PathContext paths = m_filePaths;
979 DWORD dwFlags[3] = { FFILEOPEN_NOMRU, FFILEOPEN_NOMRU, FFILEOPEN_NOMRU };
980 String strDesc[3] = { m_strDesc[0], m_strDesc[1], m_strDesc[2] };
982 GetMainFrame()->DoFileOrFolderOpen(&paths, dwFlags, strDesc, _T(""),
983 GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS), nullptr, &infoUnpacker, nullptr,
984 GetOptionsMgr()->GetBool(OPT_PLUGINS_OPEN_IN_SAME_FRAME_TYPE) ? ID_MERGE_COMPARE_HEX : -ID_MERGE_COMPARE_HEX);
988 void CHexMergeDoc::OnUpdateFileRecompareAs(CCmdUI* pCmdUI)
990 pCmdUI->Enable(pCmdUI->m_nID != ID_MERGE_COMPARE_HEX);