Revision: 5773
http://winmerge.svn.sourceforge.net/winmerge/?rev=5773&view=rev
Author: jtuc
Date: 2008-08-09 08:44:31 +0000 (Sat, 09 Aug 2008)
Log Message:
-----------
Patch #2036603
Modified Paths:
--------------
trunk/Src/DirDoc.cpp
trunk/Src/DirDoc.h
trunk/Src/DirView.cpp
trunk/Src/DirView.h
trunk/Src/MainFrm.cpp
trunk/Src/MainFrm.h
trunk/Src/Merge.cpp
trunk/Src/Merge.h
trunk/Src/Merge.rc
trunk/Src/Merge.vcproj
trunk/Src/PreLink.bat
trunk/Src/resource.h
Added Paths:
-----------
trunk/Src/HexMergeDoc.cpp
trunk/Src/HexMergeDoc.h
trunk/Src/HexMergeFrm.cpp
trunk/Src/HexMergeFrm.h
trunk/Src/HexMergeView.cpp
trunk/Src/HexMergeView.h
Modified: trunk/Src/DirDoc.cpp
===================================================================
--- trunk/Src/DirDoc.cpp 2008-08-09 08:37:04 UTC (rev 5772)
+++ trunk/Src/DirDoc.cpp 2008-08-09 08:44:31 UTC (rev 5773)
@@ -31,6 +31,7 @@
#include "StdAfx.h"
#include <shlwapi.h> // PathFindFileName()
#include "Merge.h"
+#include "HexMergeDoc.h"
#include "UnicodeString.h"
#include "CompareStats.h"
#include "FilterList.h"
@@ -86,11 +87,18 @@
delete m_pCompareStats;
// Inform all of our merge docs that we're closing
- for (POSITION pos = m_MergeDocs.GetHeadPosition(); pos; )
+ POSITION pos = m_MergeDocs.GetHeadPosition();
+ while (pos)
{
CMergeDoc * pMergeDoc = m_MergeDocs.GetNext(pos);
pMergeDoc->DirDocClosing(this);
}
+ pos = m_HexMergeDocs.GetHeadPosition();
+ while (pos)
+ {
+ CHexMergeDoc * pHexMergeDoc = m_HexMergeDocs.GetNext(pos);
+ pHexMergeDoc->DirDocClosing(this);
+ }
// Delete all temporary folders belonging to this document
while (m_pTempPathContext)
{
@@ -113,7 +121,7 @@
*/
bool CDirDoc::CanFrameClose()
{
- return !!m_MergeDocs.IsEmpty();
+ return m_MergeDocs.IsEmpty() && m_HexMergeDocs.IsEmpty();
}
/**
@@ -579,22 +587,38 @@
}
/**
+ * @brief A new HexMergeDoc has been opened.
+ */
+void CDirDoc::AddHexMergeDoc(CHexMergeDoc * pHexMergeDoc)
+{
+ ASSERT(pHexMergeDoc);
+ m_HexMergeDocs.AddTail(pHexMergeDoc);
+}
+
+/**
* @brief MergeDoc informs us it is closing.
*/
-void CDirDoc::MergeDocClosing(CMergeDoc * pMergeDoc)
+void CDirDoc::MergeDocClosing(CDocument * pMergeDoc)
{
ASSERT(pMergeDoc);
- POSITION pos = m_MergeDocs.Find(pMergeDoc);
- ASSERT(pos);
- m_MergeDocs.RemoveAt(pos);
+ if (POSITION pos = m_MergeDocs.CPtrList::Find(pMergeDoc))
+ m_MergeDocs.RemoveAt(pos);
+ else if (POSITION pos = m_HexMergeDocs.CPtrList::Find(pMergeDoc))
+ m_HexMergeDocs.RemoveAt(pos);
+ else
+ ASSERT(FALSE);
// If dir compare is empty (no compare results) and we are not closing
// because of reuse close also dir compare
- if (m_pCtxt == NULL && !m_bReuseCloses && m_pDirView)
- m_pDirView->PostMessage(WM_COMMAND, ID_FILE_CLOSE);
-
- if (m_MergeDocs.GetCount() == 0 && !m_pDirView)
+ if (m_pDirView)
+ {
+ if (m_pCtxt == NULL && !m_bReuseCloses)
+ m_pDirView->PostMessage(WM_COMMAND, ID_FILE_CLOSE);
+ }
+ else if (m_MergeDocs.GetCount() == 0 && m_HexMergeDocs.GetCount() == 0)
+ {
delete this;
+ }
}
/**
@@ -606,12 +630,20 @@
*/
BOOL CDirDoc::CloseMergeDocs()
{
- for (POSITION pos = m_MergeDocs.GetHeadPosition(); pos; )
+ POSITION pos = m_MergeDocs.GetHeadPosition();
+ while (pos)
{
CMergeDoc * pMergeDoc = m_MergeDocs.GetNext(pos);
if (!pMergeDoc->CloseNow())
return FALSE;
}
+ pos = m_HexMergeDocs.GetHeadPosition();
+ while (pos)
+ {
+ CHexMergeDoc * pHexMergeDoc = m_HexMergeDocs.GetNext(pos);
+ if (!pHexMergeDoc->CloseNow())
+ return FALSE;
+ }
return TRUE;
}
@@ -676,6 +708,36 @@
}
/**
+ * @brief Obtain a hex merge doc to display a difference in files.
+ * @param [out] pNew Set to TRUE if a new doc is created,
+ * and FALSE if an existing one reused.
+ * @return Pointer to CHexMergeDoc to use (new or existing).
+ */
+CHexMergeDoc * CDirDoc::GetHexMergeDocForDiff(BOOL * pNew)
+{
+ CHexMergeDoc * pHexMergeDoc = 0;
+ // policy -- use an existing merge doc if available
+ const BOOL bMultiDocs = GetOptionsMgr()->GetBool(OPT_MULTIDOC_MERGEDOCS);
+ if (!bMultiDocs && !m_HexMergeDocs.IsEmpty())
+ {
+ *pNew = FALSE;
+ pHexMergeDoc = m_HexMergeDocs.GetHead();
+ }
+ else
+ {
+ // Create a new merge doc
+ pHexMergeDoc = (CHexMergeDoc*)theApp.m_pHexMergeTemplate->OpenDocumentFile(NULL);
+ if (pHexMergeDoc)
+ {
+ AddHexMergeDoc(pHexMergeDoc);
+ pHexMergeDoc->SetDirDoc(this);
+ }
+ *pNew = TRUE;
+ }
+ return pHexMergeDoc;
+}
+
+/**
* @brief Update changed item's compare status
* @param [in] paths Paths for files we update
* @param [in] nDiffs Total amount of differences
Modified: trunk/Src/DirDoc.h
===================================================================
--- trunk/Src/DirDoc.h 2008-08-09 08:37:04 UTC (rev 5772)
+++ trunk/Src/DirDoc.h 2008-08-09 08:44:31 UTC (rev 5773)
@@ -35,7 +35,9 @@
class CDirView;
class CMergeDoc;
+class CHexMergeDoc;
typedef CTypedPtrList<CPtrList, CMergeDoc *> MergeDocPtrList;
+typedef CTypedPtrList<CPtrList, CHexMergeDoc *> HexMergeDocPtrList;
class DirDocFilterGlobal;
class DirDocFilterByExtension;
class CustomStatusCursor;
@@ -66,6 +68,7 @@
BOOL CloseMergeDocs();
CDirView * GetMainView();
CMergeDoc * GetMergeDocForDiff(BOOL * pNew);
+ CHexMergeDoc * GetHexMergeDocForDiff(BOOL * pNew);
BOOL ReusingDirDoc();
bool CanFrameClose();
@@ -103,7 +106,8 @@
virtual ~CDirDoc();
void SetDirView( CDirView *newView ); // TODO Perry
void AddMergeDoc(CMergeDoc * pMergeDoc);
- void MergeDocClosing(CMergeDoc * pMergeDoc);
+ void AddHexMergeDoc(CHexMergeDoc * pHexMergeDoc);
+ void MergeDocClosing(CDocument * pMergeDoc);
CDiffThread m_diffThread;
void SetDiffStatus(UINT diffcode, UINT mask, int idx);
void SetDiffCounts(UINT diffs, UINT ignored, int idx);
@@ -160,6 +164,7 @@
CDirView *m_pDirView; /**< Pointer to GUI */
CompareStats *m_pCompareStats; /**< Compare statistics */
MergeDocPtrList m_MergeDocs; /**< List of file compares opened from this compare */
+ HexMergeDocPtrList m_HexMergeDocs; /**< List of hex file compares opened from this compare */
BOOL m_bROLeft; /**< Is left side read-only */
BOOL m_bRORight; /**< Is right side read-only */
BOOL m_bRecursive; /**< Is current compare recursive? */
Modified: trunk/Src/DirView.cpp
===================================================================
--- trunk/Src/DirView.cpp 2008-08-09 08:37:04 UTC (rev 5772)
+++ trunk/Src/DirView.cpp 2008-08-09 08:44:31 UTC (rev 5773)
@@ -32,6 +32,8 @@
#include "DirView.h"
#include "DirFrame.h" // StatePane
#include "DirDoc.h"
+#include "HexMergeFrm.h"
+#include "HexMergeDoc.h"
#include "MainFrm.h"
#include "resource.h"
#include "coretools.h"
@@ -219,6 +221,8 @@
ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE, OnUpdateMergeCompare)
ON_COMMAND(ID_MERGE_COMPARE_XML, OnMergeCompareXML)
ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_XML, OnUpdateMergeCompare)
+ ON_COMMAND(ID_MERGE_COMPARE_HEX, OnMergeCompareHex)
+ ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_HEX, OnUpdateMergeCompare)
ON_COMMAND(ID_VIEW_TREEMODE, OnViewTreeMode)
ON_UPDATE_COMMAND_UI(ID_VIEW_TREEMODE, OnUpdateViewTreeMode)
ON_COMMAND(ID_VIEW_EXPAND_ALLSUBDIRS, OnViewExpandAllSubdirs)
@@ -1400,19 +1404,74 @@
DWORD leftFlags = bLeftRO ? FFILEOPEN_READONLY : 0;
DWORD rightFlags = bRightRO ? FFILEOPEN_READONLY : 0;
- int rtn = GetMainFrame()->ShowMergeDoc(pDoc, filelocLeft, filelocRight,
+ GetMainFrame()->ShowMergeDoc(pDoc, filelocLeft, filelocRight,
leftFlags, rightFlags, infoUnpacker);
- if (rtn == OPENRESULTS_FAILED_BINARY)
- {
- if (di1 == di2)
- {
- di1->diffcode.setBin();
- GetDocument()->ReloadItemStatus(sel1, FALSE, FALSE);
- }
- }
}
}
+void CDirView::OpenSelectionHex()
+{
+ CDirDoc * pDoc = GetDocument();
+
+ // First, figure out what was selected (store into pos1 & pos2)
+ POSITION pos1 = NULL, pos2 = NULL;
+ int sel1=-1, sel2=-1;
+ if (!GetSelectedItems(&sel1, &sel2))
+ {
+ // Must have 1 or 2 items selected
+ // Not valid action
+ return;
+ }
+
+ pos1 = GetItemKey(sel1);
+ ASSERT(pos1);
+ if (sel2 != -1)
+ pos2 = GetItemKey(sel2);
+
+ // Now handle the various cases of what was selected
+
+ if (pos1 == SPECIAL_ITEM_POS)
+ {
+ ASSERT(FALSE);
+ return;
+ }
+
+ // Common variables which both code paths below are responsible for setting
+ String pathLeft, pathRight;
+ DIFFITEM *di1 = NULL, *di2 = NULL; // left & right items (di1==di2 if single selection)
+ bool isdir = false; // set if we're comparing directories
+ if (pos2)
+ {
+ bool success = OpenTwoItems(pos1, pos2, &di1, &di2,
+ pathLeft, pathRight, sel1, sel2, isdir);
+ if (!success)
+ return;
+ }
+ else
+ {
+ // Only one item selected, so perform diff on its sides
+ bool success = OpenOneItem(pos1, &di1, &di2,
+ pathLeft, pathRight, sel1, isdir);
+ if (!success)
+ return;
+ }
+
+ // Need to consider only regular file case here
+
+ // Close open documents first (ask to save unsaved data)
+ if (!GetOptionsMgr()->GetBool(OPT_MULTIDOC_MERGEDOCS))
+ {
+ if (!pDoc->CloseMergeDocs())
+ return;
+ }
+
+ // Open identical and different files
+ BOOL bLeftRO = pDoc->GetReadOnly(TRUE);
+ BOOL bRightRO = pDoc->GetReadOnly(FALSE);
+
+ GetMainFrame()->ShowHexMergeDoc(pDoc, pathLeft.c_str(), pathRight.c_str(), bLeftRO, bRightRO);
+}
+
/// User chose (context menu) delete left
void CDirView::OnCtxtDirDelLeft()
{
@@ -3378,6 +3437,12 @@
OpenSelection(&packingInfo);
}
+void CDirView::OnMergeCompareHex()
+{
+ WaitStatusCursor waitstatus(IDS_STATUS_OPENING_SELECTION);
+ OpenSelectionHex();
+}
+
void CDirView::OnUpdateMergeCompare(CCmdUI *pCmdUI)
{
DoUpdateOpen(pCmdUI);
Modified: trunk/Src/DirView.h
===================================================================
--- trunk/Src/DirView.h 2008-08-09 08:37:04 UTC (rev 5772)
+++ trunk/Src/DirView.h 2008-08-09 08:44:31 UTC (rev 5773)
@@ -374,6 +374,7 @@
afx_msg void OnUpdateViewCollapseAllSubdirs(CCmdUI* pCmdUI);
afx_msg void OnMergeCompare();
afx_msg void OnMergeCompareXML();
+ afx_msg void OnMergeCompareHex();
afx_msg void OnUpdateMergeCompare(CCmdUI *pCmdUI);
afx_msg void OnViewCompareStatistics();
afx_msg void OnFileEncoding();
@@ -395,6 +396,7 @@
private:
void OpenSelection(PackingInfo * infoUnpacker = NULL);
+ void OpenSelectionHex();
bool GetSelectedItems(int * sel1, int * sel2);
void OpenParentDirectory();
void DoUpdateDirCopyRightToLeft(CCmdUI* pCmdUI, eMenuType menuType);
Added: trunk/Src/HexMergeDoc.cpp
===================================================================
--- trunk/Src/HexMergeDoc.cpp (rev 0)
+++ trunk/Src/HexMergeDoc.cpp 2008-08-09 08:44:31 UTC (rev 5773)
@@ -0,0 +1,636 @@
+/////////////////////////////////////////////////////////////////////////////
+// WinMerge: an interactive diff/merge utility
+// Copyright (C) 1997-2000 Thingamahoochie Software
+// Author: Dean Grimm
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * @file HexMergeDoc.cpp
+ *
+ * @brief Implementation file for CHexMergeDoc
+ *
+ */
+// ID line follows -- this is updated by SVN
+// $Id: $
+
+#include "stdafx.h"
+#include <afxinet.h>
+#include "UnicodeString.h"
+#include "FileTextEncoding.h"
+#include "Merge.h"
+#include "HexMergeDoc.h"
+#include "HexMergeFrm.h"
+#include "HexMergeView.h"
+#include "DiffItem.h"
+#include "FolderCmp.h"
+#include "MainFrm.h"
+#include "Environment.h"
+#include "diffcontext.h" // FILE_SAME
+#include "getopt.h"
+#include "fnmatch.h"
+#include "coretools.h"
+#include "dirdoc.h"
+#include "files.h"
+#include "OptionsDef.h"
+#include "DiffFileInfo.h"
+#include "SaveClosingDlg.h"
+#include "DiffList.h"
+#include "paths.h"
+#include "OptionsMgr.h"
+#include "FileOrFolderSelect.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+static void UpdateDiffItem(DIFFITEM &di, CDiffContext *pCtxt);
+static int Try(HRESULT hr, UINT type = MB_OKCANCEL|MB_ICONSTOP);
+
+/**
+ * @brief Update diff item
+ */
+static void UpdateDiffItem(DIFFITEM &di, CDiffContext *pCtxt)
+{
+ di.diffcode.diffcode |= DIFFCODE::SIDEFLAGS;
+ di.left.ClearPartial();
+ di.right.ClearPartial();
+ if (!pCtxt->UpdateInfoFromDiskHalf(di, TRUE))
+ di.diffcode.diffcode &= ~DIFFCODE::LEFT;
+ if (!pCtxt->UpdateInfoFromDiskHalf(di, FALSE))
+ di.diffcode.diffcode &= ~DIFFCODE::RIGHT;
+ // 1. Clear flags
+ di.diffcode.diffcode &= ~(DIFFCODE::TEXTFLAGS | DIFFCODE::COMPAREFLAGS);
+ // 2. Process unique files
+ // We must compare unique files to itself to detect encoding
+ if (di.diffcode.isSideLeftOnly() || di.diffcode.isSideRightOnly())
+ {
+ if (pCtxt->m_nCompMethod != CMP_DATE &&
+ pCtxt->m_nCompMethod != CMP_DATE_SIZE &&
+ pCtxt->m_nCompMethod != CMP_SIZE)
+ {
+ di.diffcode.diffcode |= DIFFCODE::SAME;
+ FolderCmp folderCmp;
+ int diffCode = folderCmp.prepAndCompareTwoFiles(pCtxt, di);
+ // Add possible binary flag for unique items
+ if (diffCode & DIFFCODE::BIN)
+ di.diffcode.diffcode |= DIFFCODE::BIN;
+ }
+ }
+ // 3. Compare two files
+ else
+ {
+ // Really compare
+ FolderCmp folderCmp;
+ di.diffcode.diffcode |= folderCmp.prepAndCompareTwoFiles(pCtxt, di);
+ }
+}
+
+/**
+ * @brief Issue an error popup if passed in HRESULT is nonzero
+ */
+static int Try(HRESULT hr, UINT type)
+{
+ return hr ? CInternetException(hr).ReportError(type) : 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CHexMergeDoc
+
+IMPLEMENT_DYNCREATE(CHexMergeDoc, CDocument)
+
+BEGIN_MESSAGE_MAP(CHexMergeDoc, CDocument)
+ //{{AFX_MSG_MAP(CHexMergeDoc)
+ ON_COMMAND(ID_FILE_SAVE, OnFileSave)
+ ON_COMMAND(ID_FILE_SAVE_LEFT, OnFileSaveLeft)
+ ON_COMMAND(ID_FILE_SAVE_RIGHT, OnFileSaveRight)
+ ON_COMMAND(ID_FILE_SAVEAS_LEFT, OnFileSaveAsLeft)
+ ON_COMMAND(ID_FILE_SAVEAS_RIGHT, OnFileSaveAsRight)
+ ON_UPDATE_COMMAND_UI(ID_STATUS_DIFFNUM, OnUpdateStatusNum)
+ ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_LEFT, OnUpdateFileSaveLeft)
+ ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_RIGHT, OnUpdateFileSaveRight)
+ ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
+ ON_COMMAND(ID_L2R, OnL2r)
+ ON_COMMAND(ID_R2L, OnR2l)
+ ON_COMMAND(ID_ALL_LEFT, OnAllLeft)
+ ON_COMMAND(ID_ALL_RIGHT, OnAllRight)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CHexMergeDoc construction/destruction
+
+/**
+ * @brief Constructor.
+ */
+CHexMergeDoc::CHexMergeDoc()
+: m_pDirDoc(NULL)
+{
+ m_pView[MERGE_VIEW_LEFT] = NULL;
+ m_pView[MERGE_VIEW_RIGHT] = NULL;
+}
+
+/**
+ * @brief Destructor.
+ *
+ * Informs associated dirdoc that mergedoc is closing.
+ */
+CHexMergeDoc::~CHexMergeDoc()
+{
+ if (m_pDirDoc)
+ m_pDirDoc->MergeDocClosing(this);
+}
+
+/**
+ * @brief Update associated diff item
+ */
+void CHexMergeDoc::UpdateDiffItem(CDirDoc *pDirDoc)
+{
+ // If directory compare has results
+ if (pDirDoc && pDirDoc->HasDiffs())
+ {
+ const String &pathLeft = m_filePaths.GetLeft();
+ const String &pathRight = m_filePaths.GetRight();
+ CDiffContext &ctxt = const_cast<CDiffContext &>(pDirDoc->GetDiffContext());
+ if (POSITION pos = pDirDoc->FindItemFromPaths(pathLeft.c_str(), pathRight.c_str()))
+ {
+ DIFFITEM &di = pDirDoc->GetDiffRefByKey(pos);
+ ::UpdateDiffItem(di, &ctxt);
+ }
+ }
+ int lengthLeft = m_pView[MERGE_VIEW_LEFT]->GetLength();
+ void *bufferLeft = m_pView[MERGE_VIEW_LEFT]->GetBuffer(lengthLeft);
+ int lengthRight = m_pView[MERGE_VIEW_RIGHT]->GetLength();
+ void *bufferRight = m_pView[MERGE_VIEW_RIGHT]->GetBuffer(lengthRight);
+ GetParentFrame()->SetLastCompareResult(lengthLeft != lengthRight ||
+ bufferLeft && bufferRight && memcmp(bufferLeft, bufferRight, lengthLeft));
+}
+
+/**
+ * @brief Asks and then saves modified files
+ */
+BOOL CHexMergeDoc::PromptAndSaveIfNeeded(BOOL bAllowCancel)
+{
+ const BOOL bLModified = m_pView[MERGE_VIEW_LEFT]->GetModified();
+ const BOOL bRModified = m_pView[MERGE_VIEW_RIGHT]->GetModified();
+
+ if (!bLModified && !bRModified) //Both files unmodified
+ return TRUE;
+
+ const String &pathLeft = m_filePaths.GetLeft();
+ const String &pathRight = m_filePaths.GetRight();
+
+ BOOL result = TRUE;
+ BOOL bLSaveSuccess = FALSE;
+ BOOL bRSaveSuccess = FALSE;
+
+ SaveClosingDlg dlg;
+ dlg.DoAskFor(bLModified, bRModified);
+ if (!bAllowCancel)
+ dlg.m_bDisableCancel = TRUE;
+ if (!pathLeft.empty())
+ dlg.m_sLeftFile = pathLeft.c_str();
+ else
+ dlg.m_sLeftFile = m_strDesc[0].c_str();
+ if (!pathRight.empty())
+ dlg.m_sRightFile = pathRight.c_str();
+ else
+ dlg.m_sRightFile = m_strDesc[1].c_str();
+
+ if (dlg.DoModal() == IDOK)
+ {
+ if (bLModified)
+ {
+ if (dlg.m_leftSave == SaveClosingDlg::SAVECLOSING_SAVE)
+ {
+ switch (Try(m_pView[MERGE_VIEW_LEFT]->SaveFile(pathLeft.c_str())))
+ {
+ case 0:
+ bLSaveSuccess = TRUE;
+ break;
+ case IDCANCEL:
+ result = FALSE;
+ break;
+ }
+ }
+ else
+ {
+ m_pView[MERGE_VIEW_LEFT]->SetModified(FALSE);
+ }
+ }
+ if (bRModified)
+ {
+ if (dlg.m_rightSave == SaveClosingDlg::SAVECLOSING_SAVE)
+ {
+ switch (Try(m_pView[MERGE_VIEW_RIGHT]->SaveFile(pathRight.c_str())))
+ {
+ case 0:
+ bRSaveSuccess = TRUE;
+ break;
+ case IDCANCEL:
+ result = FALSE;
+ break;
+ }
+ }
+ else
+ {
+ m_pView[MERGE_VIEW_RIGHT]->SetModified(FALSE);
+ }
+ }
+ }
+ else
+ {
+ result = FALSE;
+ }
+
+ // If file were modified and saving was successfull,
+ // update status on dir view
+ if (bLSaveSuccess || bRSaveSuccess)
+ {
+ UpdateDiffItem(m_pDirDoc);
+ }
+
+ return result;
+}
+
+/**
+ * @brief Save modified documents
+ */
+BOOL CHexMergeDoc::SaveModified()
+{
+ return PromptAndSaveIfNeeded(TRUE);
+}
+
+/**
+ * @brief Saves both files
+ */
+void CHexMergeDoc::OnFileSave()
+{
+ BOOL bUpdate = FALSE;
+ if (m_pView[MERGE_VIEW_LEFT]->GetModified())
+ {
+ const String &pathLeft = m_filePaths.GetLeft();
+ if (Try(m_pView[MERGE_VIEW_LEFT]->SaveFile(pathLeft.c_str())) == IDCANCEL)
+ return;
+ bUpdate = TRUE;
+ }
+ if (m_pView[MERGE_VIEW_RIGHT]->GetModified())
+ {
+ const String &pathRight = m_filePaths.GetRight();
+ if (Try(m_pView[MERGE_VIEW_RIGHT]->SaveFile(pathRight.c_str())) == IDCANCEL)
+ return;
+ bUpdate = TRUE;
+ }
+ if (bUpdate)
+ UpdateDiffItem(m_pDirDoc);
+}
+
+/**
+ * @brief Saves left-side file
+ */
+void CHexMergeDoc::OnFileSaveLeft()
+{
+ if (m_pView[MERGE_VIEW_LEFT]->GetModified())
+ {
+ const String &pathLeft = m_filePaths.GetLeft();
+ if (Try(m_pView[MERGE_VIEW_LEFT]->SaveFile(pathLeft.c_str())) == IDCANCEL)
+ return;
+ UpdateDiffItem(m_pDirDoc);
+ }
+}
+
+/**
+ * @brief Saves right-side file
+ */
+void CHexMergeDoc::OnFileSaveRight()
+{
+ if (m_pView[MERGE_VIEW_RIGHT]->GetModified())
+ {
+ const String &pathRight = m_filePaths.GetRight();
+ if (Try(m_pView[MERGE_VIEW_RIGHT]->SaveFile(pathRight.c_str())) == IDCANCEL)
+ return;
+ UpdateDiffItem(m_pDirDoc);
+ }
+}
+
+/**
+ * @brief Saves left-side file with name asked
+ */
+void CHexMergeDoc::OnFileSaveAsLeft()
+{
+ const String &pathLeft = m_filePaths.GetLeft();
+ CString strPath;
+ if (SelectFile(0, strPath, pathLeft.c_str(), IDS_SAVE_LEFT_AS, NULL, FALSE))
+ {
+ if (Try(m_pView[MERGE_VIEW_LEFT]->SaveFile(strPath)) == IDCANCEL)
+ return;
+ m_filePaths.SetLeft(strPath);
+ UpdateDiffItem(m_pDirDoc);
+ }
+}
+
+/**
+ * @brief Saves right-side file with name asked
+ */
+void CHexMergeDoc::OnFileSaveAsRight()
+{
+ const String &pathRight = m_filePaths.GetRight();
+ CString strPath;
+ if (SelectFile(0, strPath, pathRight.c_str(), IDS_SAVE_LEFT_AS, NULL, FALSE))
+ {
+ if (Try(m_pView[MERGE_VIEW_RIGHT]->SaveFile(strPath)) == IDCANCEL)
+ return;
+ m_filePaths.SetRight(strPath);
+ UpdateDiffItem(m_pDirDoc);
+ }
+}
+
+/**
+ * @brief Update diff-number pane text
+ */
+void CHexMergeDoc::OnUpdateStatusNum(CCmdUI* pCmdUI)
+{
+ String s;
+ pCmdUI->SetText(s.c_str());
+}
+
+/**
+ * @brief DirDoc gives us its identity just after it creates us
+ */
+void CHexMergeDoc::SetDirDoc(CDirDoc * pDirDoc)
+{
+ ASSERT(pDirDoc && !m_pDirDoc);
+ m_pDirDoc = pDirDoc;
+}
+
+/**
+ * @brief Return pointer to parent frame
+ */
+CHexMergeFrame * CHexMergeDoc::GetParentFrame()
+{
+ return static_cast<CHexMergeFrame *>(m_pView[MERGE_VIEW_LEFT]->GetParentFrame());
+}
+
+/**
+ * @brief DirDoc is closing
+ */
+void CHexMergeDoc::DirDocClosing(CDirDoc * pDirDoc)
+{
+ ASSERT(m_pDirDoc == pDirDoc);
+ m_pDirDoc = 0;
+}
+
+/**
+ * @brief DirDoc commanding us to close
+ */
+BOOL CHexMergeDoc::CloseNow()
+{
+ // Allow user to cancel closing
+ if (!PromptAndSaveIfNeeded(TRUE))
+ return FALSE;
+
+ GetParentFrame()->CloseNow();
+ return TRUE;
+}
+
+/**
+ * @brief Load files and initialize frame's compare result icon
+ */
+HRESULT CHexMergeDoc::OpenDocs(LPCTSTR pathLeft, LPCTSTR pathRight, BOOL bROLeft, BOOL bRORight)
+{
+ if (Try(m_pView[MERGE_VIEW_LEFT]->LoadFile(pathLeft), MB_ICONSTOP) == 0)
+ {
+ m_pView[MERGE_VIEW_LEFT]->SetReadOnly(bROLeft);
+ m_filePaths.SetLeft(pathLeft);
+ m_strDesc[MERGE_VIEW_LEFT] = pathLeft;
+ UpdateHeaderPath(MERGE_VIEW_LEFT);
+ }
+ if (Try(m_pView[MERGE_VIEW_RIGHT]->LoadFile(pathRight), MB_ICONSTOP) == 0)
+ {
+ m_pView[MERGE_VIEW_RIGHT]->SetReadOnly(bRORight);
+ m_filePaths.SetRight(pathRight);
+ m_strDesc[MERGE_VIEW_RIGHT] = pathRight;
+ UpdateHeaderPath(MERGE_VIEW_RIGHT);
+ }
+ m_pView[MERGE_VIEW_LEFT]->ResizeWindow();
+ m_pView[MERGE_VIEW_RIGHT]->ResizeWindow();
+ UpdateDiffItem(0);
+ if (GetOptionsMgr()->GetBool(OPT_SCROLL_TO_FIRST))
+ m_pView[MERGE_VIEW_LEFT]->SendMessage(WM_COMMAND, ID_FIRSTDIFF);
+ return S_OK;
+}
+
+/**
+ * @brief Write path and filename to headerbar
+ * @note SetText() does not repaint unchanged text
+ */
+void CHexMergeDoc::UpdateHeaderPath(int pane)
+{
+ CHexMergeFrame *pf = GetParentFrame();
+ ASSERT(pf);
+ String sText;
+
+ if (m_nBufferType[pane] == BUFFER_UNNAMED ||
+ m_nBufferType[pane] == BUFFER_NORMAL_NAMED)
+ {
+ sText = m_strDesc[pane];
+ }
+ else
+ {
+ sText = m_filePaths.GetPath(pane);
+ if (m_pDirDoc)
+ {
+ if (pane == 0)
+ m_pDirDoc->ApplyLeftDisplayRoot(sText);
+ else
+ m_pDirDoc->ApplyRightDisplayRoot(sText);
+ }
+ }
+ if (m_pView[pane]->GetModified())
+ sText.insert(0, _T("* "));
+ pf->GetHeaderInterface()->SetText(pane, sText.c_str());
+
+ SetTitle(NULL);
+}
+
+/**
+ * @brief Update document filenames to title
+ */
+void CHexMergeDoc::SetTitle(LPCTSTR lpszTitle)
+{
+ const TCHAR pszSeparator[] = _T(" - ");
+ String sTitle;
+
+ if (lpszTitle)
+ sTitle = lpszTitle;
+ else
+ {
+ if (!m_strDesc[0].empty())
+ sTitle += m_strDesc[0];
+ else
+ {
+ String file;
+ String ext;
+ SplitFilename(m_filePaths.GetLeft().c_str(), NULL, &file, &ext);
+ sTitle += file.c_str();
+ if (!ext.empty())
+ {
+ sTitle += _T(".");
+ sTitle += ext.c_str();
+ }
+ }
+
+ sTitle += pszSeparator;
+
+ if (!m_strDesc[1].empty())
+ sTitle += m_strDesc[1];
+ else
+ {
+ String file;
+ String ext;
+ SplitFilename(m_filePaths.GetRight().c_str(), NULL, &file, &ext);
+ sTitle += file.c_str();
+ if (!ext.empty())
+ {
+ sTitle += _T(".");
+ sTitle += ext.c_str();
+ }
+ }
+ }
+ CDocument::SetTitle(sTitle.c_str());
+}
+
+/**
+ * @brief We have two child views (left & right), so we keep pointers directly
+ * at them (the MFC view list doesn't have them both)
+ */
+void CHexMergeDoc::SetMergeViews(CHexMergeView * pLeft, CHexMergeView * pRight)
+{
+ ASSERT(pLeft && !m_pView[MERGE_VIEW_LEFT]);
+ m_pView[MERGE_VIEW_LEFT] = pLeft;
+ ASSERT(pRight && !m_pView[MERGE_VIEW_RIGHT]);
+ m_pView[MERGE_VIEW_RIGHT] = pRight;
+}
+
+/**
+ * @brief Called when "Save left" item is updated
+ */
+void CHexMergeDoc::OnUpdateFileSaveLeft(CCmdUI* pCmdUI)
+{
+ pCmdUI->Enable(m_pView[MERGE_VIEW_LEFT]->GetModified());
+}
+
+/**
+ * @brief Called when "Save right" item is updated
+ */
+void CHexMergeDoc::OnUpdateFileSaveRight(CCmdUI* pCmdUI)
+{
+ pCmdUI->Enable(m_pView[MERGE_VIEW_RIGHT]->GetModified());
+}
+
+/**
+ * @brief Called when "Save" item is updated
+ */
+void CHexMergeDoc::OnUpdateFileSave(CCmdUI* pCmdUI)
+{
+ const BOOL bLModified = m_pView[MERGE_VIEW_LEFT]->GetModified();
+ const BOOL bRModified = m_pView[MERGE_VIEW_RIGHT]->GetModified();
+ pCmdUI->Enable(bLModified || bRModified);
+}
+
+/**
+ * @brief Copy selected bytes from source view to destination view
+ * @note Grows destination buffer as appropriate
+ */
+void CHexMergeDoc::CopySel(CHexMergeView *pViewSrc, CHexMergeView *pViewDst)
+{
+ const IHexEditorWindow::Status *pStatSrc = pViewSrc->GetStatus();
+ int i = min(pStatSrc->iStartOfSelection, pStatSrc->iEndOfSelection);
+ int j = max(pStatSrc->iStartOfSelection, pStatSrc->iEndOfSelection);
+ int u = pViewSrc->GetLength();
+ int v = pViewDst->GetLength();
+ if (pStatSrc->bSelected && i <= v)
+ {
+ if (v <= j)
+ v = j + 1;
+ BYTE *p = pViewSrc->GetBuffer(u);
+ BYTE *q = pViewDst->GetBuffer(v);
+ memcpy(q + i, p + i, j - i + 1);
+ CWnd *pwndFocus = CWnd::GetFocus();
+ if (pwndFocus != pViewSrc)
+ pViewDst->RepaintRange(i, j);
+ if (pwndFocus != pViewDst)
+ pViewSrc->RepaintRange(i, j);
+ pViewDst->SetModified(TRUE);
+ }
+}
+
+/**
+ * @brief Copy all bytes from source view to destination view
+ * @note Grows destination buffer as appropriate
+ */
+void CHexMergeDoc::CopyAll(CHexMergeView *pViewSrc, CHexMergeView *pViewDst)
+{
+ if (int i = pViewSrc->GetLength())
+ {
+ int j = pViewDst->GetLength();
+ BYTE *p = pViewSrc->GetBuffer(i);
+ BYTE *q = pViewDst->GetBuffer(max(i, j));
+ if (q == 0)
+ AfxThrowMemoryException();
+ memcpy(q, p, i);
+ CWnd *pwndFocus = CWnd::GetFocus();
+ if (pwndFocus != pViewSrc)
+ pViewDst->RepaintRange(0, i);
+ if (pwndFocus != pViewDst)
+ pViewSrc->RepaintRange(0, i);
+ pViewDst->SetModified(TRUE);
+ }
+}
+
+/**
+ * @brief Copy selected bytes from left to right
+ */
+void CHexMergeDoc::OnL2r()
+{
+ CopySel(m_pView[MERGE_VIEW_LEFT], m_pView[MERGE_VIEW_RIGHT]);
+}
+
+/**
+ * @brief Copy selected bytes from right to left
+ */
+void CHexMergeDoc::OnR2l()
+{
+ CopySel(m_pView[MERGE_VIEW_RIGHT], m_pView[MERGE_VIEW_LEFT]);
+}
+
+/**
+ * @brief Copy all bytes from left to right
+ */
+void CHexMergeDoc::OnAllRight()
+{
+ CopyAll(m_pView[MERGE_VIEW_LEFT], m_pView[MERGE_VIEW_RIGHT]);
+}
+
+/**
+ * @brief Copy all bytes from right to left
+ */
+void CHexMergeDoc::OnAllLeft()
+{
+ CopyAll(m_pView[MERGE_VIEW_RIGHT], m_pView[MERGE_VIEW_LEFT]);
+}
Added: trunk/Src/HexMergeDoc.h
===================================================================
--- trunk/Src/HexMergeDoc.h (rev 0)
+++ trunk/Src/HexMergeDoc.h 2008-08-09 08:44:31 UTC (rev 5773)
@@ -0,0 +1,109 @@
+/////////////////////////////////////////////////////////////////////////////
+// WinMerge: an interactive diff/merge utility
+// Copyright (C) 1997 Dean P. Grimm
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * @file HexMergeDoc.h
+ *
+ * @brief Declaration of CHexMergeDoc class
+ */
+// RCS ID line follows -- this is updated by CVS
+// $Id: $
+
+#include "TempFile.h"
+#include "PathContext.h"
+#include "DiffFileInfo.h"
+
+class CDirDoc;
+class CHexMergeFrame;
+class CHexMergeView;
+
+/**
+ * @brief Document class for bytewise merging two files presented as hexdumps
+ */
+class CHexMergeDoc : public CDocument
+{
+// Attributes
+public:
+ PathContext m_filePaths; /**< Filepaths for this document */
+
+// Begin declaration of CHexMergeDoc
+
+protected: // create from serialization only
+ CHexMergeDoc();
+ DECLARE_DYNCREATE(CHexMergeDoc)
+
+
+ // Operations
+public:
+ void SetMergeViews(CHexMergeView * pLeft, CHexMergeView * pRight);
+
+ // Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CMergeDoc)
+ public:
+ virtual BOOL SaveModified();
+ virtual void SetTitle(LPCTSTR lpszTitle);
+ //}}AFX_VIRTUAL
+
+// Implementation
+public:
+ ~CHexMergeDoc();
+ void UpdateDiffItem(CDirDoc * pDirDoc);
+ BOOL PromptAndSaveIfNeeded(BOOL bAllowCancel);
+ void SetDirDoc(CDirDoc * pDirDoc);
+ void DirDocClosing(CDirDoc * pDirDoc);
+ BOOL CloseNow();
+ CHexMergeFrame * GetParentFrame();
+ void UpdateHeaderPath(int pane);
+ HRESULT OpenDocs(LPCTSTR pathLeft, LPCTSTR pathRight, BOOL bROLeft, BOOL bRORight);
+protected:
+ static void CopySel(CHexMergeView *pViewSrc, CHexMergeView *pViewDst);
+ static void CopyAll(CHexMergeView *pViewSrc, CHexMergeView *pViewDst);
+// Implementation data
+protected:
+ CHexMergeView * m_pView[MERGE_VIEW_COUNT]; /**< Pointer to left/right view */
+ CDirDoc * m_pDirDoc;
+ TempFile m_tempFiles[2]; /**< Temp files for compared files */
+ String m_strDesc[2]; /**< Left/right side description text */
+ BUFFERTYPE m_nBufferType[2];
+
+// Generated message map functions
+protected:
+ //{{AFX_MSG(CMergeDoc)
+ afx_msg void OnFileSave();
+ afx_msg void OnFileSaveLeft();
+ afx_msg void OnFileSaveRight();
+ afx_msg void OnFileSaveAsLeft();
+ afx_msg void OnFileSaveAsRight();
+ afx_msg void OnUpdateStatusNum(CCmdUI* pCmdUI);
+ afx_msg void OnUpdateFileSaveLeft(CCmdUI* pCmdUI);
+ afx_msg void OnUpdateFileSaveRight(CCmdUI* pCmdUI);
+ afx_msg void OnUpdateFileSave(CCmdUI* pCmdUI);
+ afx_msg void OnL2r();
+ afx_msg void OnR2l();
+ afx_msg void OnAllRight();
+ afx_msg void OnAllLeft();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
Added: trunk/Src/HexMergeFrm.cpp
===================================================================
--- trunk/Src/HexMergeFrm.cpp (rev 0)
+++ trunk/Src/HexMergeFrm.cpp 2008-08-09 08:44:31 UTC (rev 5773)
@@ -0,0 +1,479 @@
+/////////////////////////////////////////////////////////////////////////////
+// WinMerge: an interactive diff/merge utility
+// Copyright (C) 1997-2000 Thingamahoochie Software
+// Author: Dean Grimm
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * @file HexMergeFrm.cpp
+ *
+ * @brief Implementation file for CHexMergeFrame
+ *
+ */
+// ID line follows -- this is updated by SVN
+// $Id: $
+
+#include "stdafx.h"
+#include "Merge.h"
+#include "MainFrm.h"
+#include "HexMergeFrm.h"
+#include "HexMergeDoc.h"
+#include "HexMergeView.h"
+#include "OptionsDef.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/**
+ * @brief RO status panel width
+ */
+static UINT RO_PANEL_WIDTH = 40;
+
+/////////////////////////////////////////////////////////////////////////////
+// CHexMergeFrame
+
+IMPLEMENT_DYNCREATE(CHexMergeFrame, CMDIChildWnd)
+
+BEGIN_MESSAGE_MAP(CHexMergeFrame, CMDIChildWnd)
+ //{{AFX_MSG_MAP(CHexMergeFrame)
+ ON_WM_CREATE()
+ ON_WM_CLOSE()
+ ON_WM_SIZE()
+ ON_MESSAGE_VOID(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
+ ON_UPDATE_COMMAND_UI(ID_VIEW_DETAIL_BAR, OnUpdateControlBarMenu)
+ ON_COMMAND_EX(ID_VIEW_DETAIL_BAR, OnBarCheck)
+ ON_UPDATE_COMMAND_UI(ID_VIEW_LOCATION_BAR, OnUpdateControlBarMenu)
+ ON_COMMAND_EX(ID_VIEW_LOCATION_BAR, OnBarCheck)
+ ON_MESSAGE(MSG_STORE_PANESIZES, OnStorePaneSizes)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/**
+ * @brief Statusbar pane indexes
+ */
+enum
+{
+ PANE_LEFT_INFO = 0,
+ PANE_LEFT_RO,
+ PANE_LEFT_EOL,
+ PANE_RIGHT_INFO,
+ PANE_RIGHT_RO,
+ PANE_RIGHT_EOL,
+};
+
+/**
+ * @brief Bottom statusbar panels and indicators
+ */
+static UINT indicatorsBottom[] =
+{
+ ID_SEPARATOR,
+ ID_SEPARATOR,
+ ID_SEPARATOR,
+ ID_SEPARATOR,
+ ID_SEPARATOR,
+ ID_SEPARATOR,
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// CHexMergeFrame construction/destruction
+
+CHexMergeFrame::CHexMergeFrame()
+: m_hIdentical(NULL)
+, m_hDifferent(NULL)
+{
+ m_bActivated = FALSE;
+ m_nLastSplitPos = 0;
+ m_pMergeDoc = 0;
+}
+
+CHexMergeFrame::~CHexMergeFrame()
+{
+}
+
+/**
+ * @brief Customize a heksedit control's settings
+ */
+static void Customize(IHexEditorWindow::Settings *settings)
+{
+ settings->bSaveIni = FALSE;
+ settings->iAutomaticBPL = FALSE;
+ settings->iBytesPerLine = 16;
+ settings->iFontSize = 8;
+}
+
+/**
+ * @brief Customize a heksedit control's colors
+ */
+static void Customize(IHexEditorWindow::Colors *colors)
+{
+ COptionsMgr *pOptionsMgr = GetOptionsMgr();
+ colors->iSelBkColorValue = RGB(224, 224, 224);
+ colors->iDiffBkColorValue = pOptionsMgr->GetInt(OPT_CLR_DIFF);
+ colors->iSelDiffBkColorValue = pOptionsMgr->GetInt(OPT_CLR_SELECTED_DIFF);
+ colors->iDiffTextColorValue = pOptionsMgr->GetInt(OPT_CLR_DIFF_TEXT);
+ colors->iSelDiffTextColorValue = pOptionsMgr->GetInt(OPT_CLR_SELECTED_DIFF_TEXT);
+}
+
+/**
+ * @brief Customize a heksedit control's settings and colors
+ */
+static void Customize(IHexEditorWindow *pif)
+{
+ Customize(pif->get_settings());
+ Customize(pif->get_colors());
+}
+
+/**
+ * @brief Create a status bar to be associated with a heksedit control
+ */
+void CHexMergeFrame::CreateHexWndStatusBar(CStatusBar &wndStatusBar)
+{
+ wndStatusBar.Create(this, WS_CHILD|WS_VISIBLE);
+ wndStatusBar.SetIndicators(0, 3);
+ wndStatusBar.SetPaneInfo(0, 0, SBPS_STRETCH, 0);
+ wndStatusBar.SetPaneInfo(1, 0, 0, 72);
+ wndStatusBar.SetPaneInfo(2, 0, 0, 72);
+}
+
+/**
+ * @brief Create the splitter, the filename bar, the status bar, and the two views
+ */
+BOOL CHexMergeFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
+ CCreateContext* pContext)
+{
+ // create a splitter with 1 row, 2 columns
+ if (!m_wndSplitter.CreateStatic(this, 1, 2, WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL) )
+ {
+ TRACE0("Failed to CreateStaticSplitter\n");
+ return FALSE;
+ }
+
+ if (!m_wndSplitter.CreateView(0, 0,
+ RUNTIME_CLASS(CHexMergeView), CSize(-1, 200), pContext))
+ {
+ TRACE0("Failed to create first pane\n");
+ return FALSE;
+ }
+
+ // add the second splitter pane - an input view in column 1
+ if (!m_wndSplitter.CreateView(0, 1,
+ RUNTIME_CLASS(CHexMergeView), CSize(-1, 200), pContext))
+ {
+ TRACE0("Failed to create second pane\n");
+ return FALSE;
+ }
+ m_wndSplitter.ResizablePanes(TRUE);
+ m_wndSplitter.AutoResizePanes(GetOptionsMgr()->GetBool(OPT_RESIZE_PANES));
+
+ // Merge frame has a header bar at top
+ if (!m_wndFilePathBar.Create(this))
+ {
+ TRACE0("Failed to create dialog bar\n");
+ return FALSE; // fail to create
+ }
+ // Set filename bars inactive so colors get initialized
+ m_wndFilePathBar.SetActive(0, FALSE);
+ m_wndFilePathBar.SetActive(1, FALSE);
+
+ m_wndLeftStatusBar.m_cxRightBorder = 4;
+ ModifyStyle(WS_THICKFRAME, 0); // Prevent SBARS_SIZEGRIP
+ CreateHexWndStatusBar(m_wndLeftStatusBar);
+ ModifyStyle(0, WS_THICKFRAME);
+ CreateHexWndStatusBar(m_wndRightStatusBar);
+ CSize size = m_wndLeftStatusBar.CalcFixedLayout(TRUE, TRUE);
+ m_rectBorder.bottom = size.cy;
+
+ m_hIdentical = AfxGetApp()->LoadIcon(IDI_EQUALFILE);
+ m_hDifferent = AfxGetApp()->LoadIcon(IDI_NOTEQUALFILE);
+
+ // stash left & right pointers into the mergedoc
+ CHexMergeView *pLeft = static_cast<CHexMergeView *>(m_wndSplitter.GetPane(0,0));
+ CHexMergeView *pRight = static_cast<CHexMergeView *>(m_wndSplitter.GetPane(0,1));
+
+ IHexEditorWindow *pifLeft = reinterpret_cast<IHexEditorWindow *>(
+ ::GetWindowLongPtr(pLeft->m_hWnd, GWL_USERDATA));
+ IHexEditorWindow *pifRight = reinterpret_cast<IHexEditorWindow *>(
+ ::GetWindowLongPtr(pRight->m_hWnd, GWL_USERDATA));
+
+ if (pifLeft && pifRight)
+ {
+ pifLeft->set_sibling(pifRight);
+ pifRight->set_sibling(pifLeft);
+ pifLeft->set_status_bar(m_wndLeftStatusBar.m_hWnd);
+ pifRight->set_status_bar(m_wndRightStatusBar.m_hWnd);
+ Customize(pifLeft);
+ Customize(pifRight);
+ }
+
+ // tell merge doc about these views
+ m_pMergeDoc = dynamic_cast<CHexMergeDoc *>(pContext->m_pCurrentDoc);
+ m_pMergeDoc->SetMergeViews(pLeft, pRight);
+ pLeft->m_nThisPane = 0;
+ pRight->m_nThisPane = 1;
+
+ return TRUE;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CHexMergeFrame message handlers
+
+/**
+ * @brief Handle translation of default messages on the status bar
+ */
+void CHexMergeFrame::GetMessageString(UINT nID, CString& rMessage) const
+{
+ // load appropriate string
+ const String s = theApp.LoadString(nID);
+ if (!AfxExtractSubString(rMessage, &*s.begin(), 0))
+ {
+ // not found
+ TRACE1("Warning: no message line prompt for ID 0x%04X.\n", nID);
+ }
+}
+
+/**
+ * @brief Save the window's position, free related resources, and destroy the window
+ */
+BOOL CHexMergeFrame::DestroyWindow()
+{
+ SavePosition();
+ // If we are active, save the restored/maximized state
+ // If we are not, do nothing and let the active frame do the job.
+ if (GetParentFrame()->GetActiveFrame() == this)
+ {
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(WINDOWPLACEMENT);
+ GetWindowPlacement(&wp);
+ theApp.WriteProfileInt(_T("Settings"), _T("ActiveFrameMax"), (wp.showCmd == SW_MAXIMIZE));
+ }
+
+ if (m_hIdentical != NULL)
+ {
+ DestroyIcon(m_hIdentical);
+ m_hIdentical = NULL;
+ }
+
+ if (m_hDifferent != NULL)
+ {
+ DestroyIcon(m_hDifferent);
+ m_hDifferent = NULL;
+ }
+
+ return CMDIChildWnd::DestroyWindow();
+}
+
+/**
+ * @brief Save coordinates of the frame, splitters, and bars
+ *
+ * @note Do not save the maximized/restored state here. We are interested
+ * in the state of the active frame, and maybe this frame is not active
+ */
+void CHexMergeFrame::SavePosition()
+{
+ if (CWnd *pLeft = m_wndSplitter.GetPane(0,0))
+ {
+ CRect rc;
+ pLeft->GetWindowRect(&rc);
+ theApp.WriteProfileInt(_T("Settings"), _T("WLeft"), rc.Width());
+ }
+}
+
+void CHexMergeFrame::OnSize(UINT nType, int cx, int cy)
+{
+ CMDIChildWnd::OnSize(nType, cx, cy);
+ UpdateHeaderSizes();
+}
+
+/// update splitting position for panels 1/2 and headerbar and statusbar
+void CHexMergeFrame::UpdateHeaderSizes()
+{
+ if (IsWindowVisible())
+ {
+ int w0, w1, wmin;
+ m_wndSplitter.GetColumnInfo(0, w0, wmin);
+ m_wndSplitter.GetColumnInfo(1, w1, wmin);
+ if (w0 < 1) w0 = 1; // Perry 2003-01-22 (I don't know why this happens)
+ if (w1 < 1) w1 = 1; // Perry 2003-01-22 (I don't know why this happens)
+ // resize controls in header dialog bar
+ m_wndFilePathBar.Resize(w0, w1);
+ RECT rc;
+ GetClientRect(&rc);
+ rc.top = rc.bottom - m_rectBorder.bottom;
+ rc.left = w0 + 8;
+ m_wndRightStatusBar.MoveWindow(&rc);
+ rc.right = rc.left;
+ rc.left = 0;
+ m_wndLeftStatusBar.MoveWindow(&rc);
+ }
+}
+
+IHeaderBar *CHexMergeFrame::GetHeaderInterface()
+{
+ return &m_wndFilePathBar;
+}
+
+/**
+ * @brief Reflect comparison result in window's icon.
+ * @param nResult [in] Last comparison result which the application returns.
+ */
+void CHexMergeFrame::SetLastCompareResult(int nResult)
+{
+ HICON hCurrent = GetIcon(FALSE);
+ HICON hReplace = (nResult == 0) ? m_hIdentical : m_hDifferent;
+
+ if (hCurrent != hReplace)
+ {
+ SetIcon(hReplace, TRUE);
+
+ BOOL bMaximized;
+ GetMDIFrame()->MDIGetActive(&bMaximized);
+
+ // When MDI maximized the window icon is drawn on the menu bar, so we
+ // need to notify it that our icon has changed.
+ if (bMaximized)
+ {
+ GetMDIFrame()->DrawMenuBar();
+ }
+ }
+
+ theApp.SetLastCompareResult(nResult);
+}
+
+void CHexMergeFrame::UpdateAutoPaneResize()
+{
+ m_wndSplitter.AutoResizePanes(GetOptionsMgr()->GetBool(OPT_RESIZE_PANES));
+}
+
+void CHexMergeFrame::UpdateSplitter()
+{
+ m_wndSplitter.RecalcLayout();
+}
+
+/**
+ * @brief Synchronize control and status bar placements with splitter position,
+ * update mod indicators, synchronize scrollbars
+ */
+void CHexMergeFrame::OnIdleUpdateCmdUI()
+{
+ if (IsWindowVisible())
+ {
+ int w,wmin;
+ m_wndSplitter.GetColumnInfo(0, w, wmin);
+ if (w != m_nLastSplitPos && w > 0)
+ {
+ UpdateHeaderSizes();
+ m_nLastSplitPos = w;
+ }
+ CHexMergeView *pLeft = (CHexMergeView *)m_wndSplitter.GetPane(0, MERGE_VIEW_LEFT);
+ CHexMergeView *pRight = (CHexMergeView *)m_wndSplitter.GetPane(0, MERGE_VIEW_RIGHT);
+
+ // Update mod indicators
+ TCHAR ind[2];
+
+ if (m_wndFilePathBar.GetDlgItemText(IDC_STATIC_TITLE_LEFT, ind, 2))
+ if (pLeft->GetModified() ? ind[0] != _T('*') : ind[0] == _T('*'))
+ m_pMergeDoc->UpdateHeaderPath(MERGE_VIEW_LEFT);
+
+ if (m_wndFilePathBar.GetDlgItemText(IDC_STATIC_TITLE_RIGHT, ind, 2))
+ if (pRight->GetModified() ? ind[0] != _T('*') : ind[0] == _T('*'))
+ m_pMergeDoc->UpdateHeaderPath(MERGE_VIEW_RIGHT);
+
+ // Synchronize scrollbars
+ SCROLLINFO si, siLeft, siRight;
+ // Synchronize horizontal scrollbars
+ pLeft->GetScrollInfo(SB_HORZ, &si, SIF_ALL | SIF_DISABLENOSCROLL);
+ siLeft = si;
+ pRight->GetScrollInfo(SB_HORZ, &si, SIF_ALL | SIF_DISABLENOSCROLL);
+ siRight = si;
+ if (si.nMin > siLeft.nMin)
+ si.nMin = siLeft.nMin;
+ if (si.nPage < siLeft.nPage)
+ si.nPage = siLeft.nPage;
+ if (si.nMax < siLeft.nMax)
+ si.nMax = siLeft.nMax;
+ if (GetFocus() != pRight)
+ {
+ si.nPos = siLeft.nPos;
+ si.nTrackPos = siLeft.nTrackPos;
+ }
+ if (memcmp(&si, &siLeft, sizeof si))
+ {
+ pLeft->SetScrollInfo(SB_HORZ, &si);
+ pLeft->SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBTRACK, si.nTrackPos));
+ }
+ if (memcmp(&si, &siRight, sizeof si))
+ {
+ pRight->SetScrollInfo(SB_HORZ, &si);
+ pRight->SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBTRACK, si.nTrackPos));
+ }
+ m_wndSplitter.GetScrollBarCtrl(pLeft, SB_HORZ)->SetScrollInfo(&si);
+ m_wndSplitter.GetScrollBarCtrl(pRight, SB_HORZ)->SetScrollInfo(&si);
+ // Synchronize vertical scrollbars
+ pLeft->GetScrollInfo(SB_VERT, &si, SIF_ALL | SIF_DISABLENOSCROLL);
+ siLeft = si;
+ pRight->GetScrollInfo(SB_VERT, &si, SIF_ALL | SIF_DISABLENOSCROLL);
+ siRight = si;
+ if (si.nMin > siLeft.nMin)
+ si.nMin = siLeft.nMin;
+ if (si.nMax < siLeft.nMax)
+ si.nMax = siLeft.nMax;
+ if (GetFocus() != pRight)
+ {
+ si.nPos = siLeft.nPos;
+ si.nTrackPos = siLeft.nTrackPos;
+ }
+ if (memcmp(&si, &siLeft, sizeof si))
+ {
+ pLeft->SetScrollInfo(SB_VERT, &si);
+ pLeft->SendMessage(WM_VSCROLL, MAKEWPARAM(SB_THUMBTRACK, si.nTrackPos));
+ }
+ if (memcmp(&si, &siRight, sizeof si))
+ {
+ pRight->SetScrollInfo(SB_VERT, &si);
+ pRight->SendMessage(WM_VSCROLL, MAKEWPARAM(SB_THUMBTRACK, si.nTrackPos));
+ }
+ m_wndSplitter.GetScrollBarCtrl(pRight, SB_VERT)->SetScrollInfo(&si);
+ }
+ CMDIChildWnd::OnIdleUpdateCmdUI();
+}
+
+/// Document commanding us to close
+void CHexMergeFrame::CloseNow()
+{
+ SavePosition(); // Save settings before closing!
+ MDIActivate();
+ MDIDestroy();
+}
+
+/**
+ * @brief Update any resources necessary after a GUI language change
+ */
+void CHexMergeFrame::UpdateResources()
+{
+}
+
+/**
+ * @brief Save pane sizes and positions when one of panes requests it.
+ */
+LRESULT CHexMergeFrame::OnStorePaneSizes(WPARAM wParam, LPARAM lParam)
+{
+ SavePosition();
+ return 0;
+}
Added: trunk/Src/HexMergeFrm.h
===================================================================
--- trunk/Src/HexMergeFrm.h (rev 0)
+++ trunk/Src/HexMergeFrm.h 2008-08-09 08:44:31 UTC (rev 5773)
@@ -0,0 +1,99 @@
+/////////////////////////////////////////////////////////////////////////////
+// WinMerge: an interactive diff/merge utility
+// Copyright (C) 1997 Dean P. Grimm
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * @file ChildFrm.h
+ *
+ * @brief interface of the CHexMergeFrame class
+ *
+ */
+// ID line follows -- this is updated by SVN
+// $Id: ChildFrm.h 4617 2007-10-14 08:25:48Z jtuc $
+
+#include "SplitterWndEx.h"
+#include "EditorFilepathBar.h"
+#include "../externals/heksedit/heksedit.h"
+
+class CHexMergeDoc;
+
+/**
+ * @brief Frame class for file compare, handles panes, statusbar etc.
+ */
+class CHexMergeFrame : public CMDIChildWnd
+{
+ DECLARE_DYNCREATE(CHexMergeFrame)
+public:
+ CHexMergeFrame();
+
+// Operations
+public:
+ void UpdateResources();
+ void CloseNow();
+ IHeaderBar * GetHeaderInterface();
+ void SetSharedMenu(HMENU hMenu) { m_hMenuShared = hMenu; };
+ CHexMergeDoc * GetMergeDoc() { return m_pMergeDoc; }
+ void SetLastCompareResult(int nResult);
+
+ void UpdateAutoPaneResize();
+ void UpdateSplitter();
+
+
+// Attributes
+protected:
+ CSplitterWndEx m_wndSplitter;
+ CEditorFilePathBar m_wndFilePathBar;
+ CStatusBar m_wndLeftStatusBar;
+ CStatusBar m_wndRightStatusBar;
+// Overrides
+public:
+ virtual void GetMessageString(UINT nID, CString& rMessage) const;
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CHexMergeFrame)
+ public:
+ virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext);
+ virtual BOOL DestroyWindow();
+ protected:
+ //}}AFX_VIRTUAL
+
+// Implementation
+private:
+ void SavePosition();
+ virtual ~CHexMergeFrame();
+ void CreateHexWndStatusBar(CStatusBar &);
+// Generated message map functions
+private:
+ int m_nLastSplitPos;
+ void UpdateHeaderSizes();
+ BOOL m_bActivated;
+ CHexMergeDoc * m_pMergeDoc;
+ HICON m_hIdentical;
+ HICON m_hDifferent;
+
+ //{{AFX_MSG(CHexMergeFrame)
+ afx_msg void OnSize(UINT nType, int cx, int cy);
+ afx_msg void OnIdleUpdateCmdUI();
+ afx_msg LRESULT OnStorePaneSizes(WPARAM wParam, LPARAM lParam);
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
Added: trunk/Src/HexMergeView.cpp
===================================================================
--- trunk/Src/HexMergeView.cpp (rev 0)
+++ trunk/Src/HexMergeView.cpp 2008-08-09 08:44:31 UTC (rev 5773)
@@ -0,0 +1,508 @@
+/////////////////////////////////////////////////////////////////////////////
+// WinMerge: an interactive diff/merge utility
+// Copyright (C) 1997-2000 Thingamahoochie Software
+// Author: Dean Grimm
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * @file HexMergeView.cpp
+ *
+ * @brief Implementation file for CHexMergeDoc
+ *
+ */
+// ID line follows -- this is updated by SVN
+// $Id: $
+
+#include "stdafx.h"
+#include "Merge.h"
+#include "MainFrm.h"
+#include "HexMergeFrm.h"
+#include "HexMergeView.h"
+#include "OptionsDef.h"
+#include "Environment.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/**
+ * @brief Turn bool api result into success/error code.
+ */
+static HRESULT NTAPI SE(BOOL f)
+{
+ if (f)
+ return S_OK;
+ HRESULT hr = (HRESULT)::GetLastError();
+ ASSERT(hr);
+ if (hr == 0)
+ hr = E_UNEXPECTED;
+ return hr;
+}
+
+static UINT64 NTAPI GetLastWriteTime(HANDLE h)
+{
+ UINT64 ft;
+ return ::GetFileTime(h, 0, 0, reinterpret_cast<FILETIME *>(&ft)) ? ft : 0;
+}
+
+static void NTAPI SetLastWriteTime(HANDLE h, UINT64 ft)
+{
+ ::SetFileTime(h, 0, 0, reinterpret_cast<FILETIME *>(&ft));
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CHexMergeView
+
+IMPLEMENT_DYNCREATE(CHexMergeView, CView)
+
+BEGIN_MESSAGE_MAP(CHexMergeView, CView)
+ //{{AFX_MSG_MAP(CHexMergeView)
+ ON_MESSAGE_VOID(WM_PAINT, CWnd::OnPaint)
+ ON_WM_CREATE()
+ ON_WM_HSCROLL()
+ ON_WM_VSCROLL()
+ ON_WM_NCCALCSIZE()
+ ON_COMMAND(ID_EDIT_FIND, OnEditFind)
+ ON_COMMAND(ID_EDIT_REPLACE, OnEditReplace)
+ ON_COMMAND(ID_EDIT_REPEAT, OnEditRepeat)
+ ON_COMMAND(ID_EDIT_CUT, OnEditCut)
+ ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
+ ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
+ ON_COMMAND(ID_EDIT_CLEAR, OnEditClear)
+ ON_COMMAND(ID_FIRSTDIFF, OnFirstdiff)
+ ON_COMMAND(ID_LASTDIFF, OnLastdiff)
+ ON_COMMAND(ID_NEXTDIFF, OnNextdiff)
+ ON_COMMAND(ID_PREVDIFF, OnPrevdiff)
+ //}}AFX_MSG_MAP
+ // Test case to verify WM_COMMAND won't accidentally go through Default()
+ //ON_COMMAND(ID_APP_ABOUT, Default)
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CHexMergeView construction/destruction
+
+/**
+ * @brief Constructor.
+ */
+CHexMergeView::CHexMergeView()
+: m_pif(0)
+, m_nThisPane(0)
+, m_mtime(0)
+{
+}
+
+/**
+ * @brief Drawing is not supported
+ */
+void CHexMergeView::OnDraw(CDC *)
+{
+ ASSERT(FALSE);
+}
+
+/**
+ * @brief Load heksedit.dll and setup window class name
+ */
+BOOL CHexMergeView::PreCreateWindow(CREATESTRUCT& cs)
+{
+ static const TCHAR szFileName[] = _T("heksedit.dll");
+ static const TCHAR szClassName[] = _T("frhed hexclass");
+ if ((cs.hInstance = ::GetModuleHandle(szFileName)) == 0 &&
+ (cs.hInstance = ::LoadLibrary(szFileName)) == 0)
+ {
+ return FALSE;
+ }
+ cs.lpszClass = szClassName;
+ cs.style |= WS_HSCROLL | WS_VSCROLL;
+ return TRUE;
+}
+
+/**
+ * @brief Grab the control's IHexEditorWindow interface pointer upon window creation
+ */
+int CHexMergeView::OnCreate(LPCREATESTRUCT lpCreateStruct)
+{
+ if (CView::OnCreate(lpCreateStruct) == -1)
+ return -1;
+ m_pif = reinterpret_cast<IHexEditorWindow *>(::GetWindowLongPtr(m_hWnd, GWL_USERDATA));
+ if (m_pif == 0)
+ return -1;
+ return 0;
+}
+
+/**
+ * @brief Skip default WM_NCCALCSIZE processing so as to prevent scrollbars from showing up
+ */
+void CHexMergeView::OnNcCalcSize(BOOL, NCCALCSIZE_PARAMS *)
+{
+}
+
+/**
+ * @brief Synchronize all involved scrollbars
+ */
+void CHexMergeView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar * pScrollBar)
+{
+ SCROLLINFO si;
+ if (pScrollBar && nSBCode == SB_THUMBTRACK)
+ {
+ pScrollBar->GetScrollInfo(&si, SIF_ALL | SIF_DISABLENOSCROLL);
+ si.nPos = si.nTrackPos;
+ SetScrollInfo(SB_HORZ, &si);
+ }
+ CView::OnHScroll(nSBCode, nPos, pScrollBar);
+ if (pScrollBar)
+ {
+ GetScrollInfo(SB_HORZ, &si, SIF_ALL | SIF_DISABLENOSCROLL);
+ if (nSBCode != SB_THUMBTRACK)
+ {
+ pScrollBar->SetScrollInfo(&si);
+ }
+ CSplitterWndEx *pSplitter = (CSplitterWndEx *)GetParentSplitter(this, TRUE);
+ int nID = GetDlgCtrlID();
+ nID ^= pSplitter->IdFromRowCol(0, 0) ^ pSplitter->IdFromRowCol(0, 1);
+ CWnd *pWnd = pSplitter->GetDlgItem(nID);
+ pWnd->SetScrollInfo(SB_HORZ, &si);
+ pWnd->SendMessage(WM_HSCROLL, MAKEWPARAM(nSBCode, nPos));
+ }
+}
+
+/**
+ * @brief Synchronize all involved scrollbars
+ */
+void CHexMergeView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar * pScrollBar)
+{
+ SCROLLINFO si;
+ if (pScrollBar && nSBCode == SB_THUMBTRACK)
+ {
+ pScrollBar->GetScrollInfo(&si);
+ si.nPos = si.nTrackPos;
+ SetScrollInfo(SB_VERT, &si, SIF_ALL | SIF_DISABLENOSCROLL);
+ }
+ CView::OnVScroll(nSBCode, nPos, pScrollBar);
+ if (pScrollBar && nSBCode != SB_THUMBTRACK)
+ {
+ GetScrollInfo(SB_VERT, &si);
+ pScrollBar->SetScrollInfo(&si, SIF_ALL | SIF_DISABLENOSCROLL);
+ }
+}
+
+/**
+ * @brief Synchronize file path bar activation states
+ */
+void CHexMergeView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
+{
+ CView::OnActivateView(bActivate, pActivateView, pDeactiveView);
+ CHexMergeFrame *pFrameWnd = static_cast<CHexMergeFrame *>(GetParentFrame());
+ pFrameWnd->GetHeaderInterface()->SetActive(m_nThisPane, bActivate);
+}
+
+/**
+ * @brief Get pointer to control's content buffer
+ */
+BYTE *CHexMergeView::GetBuffer(int length)
+{
+ return m_pif->get_buffer(length);
+}
+
+/**
+ * @brief Get length of control's content buffer
+ */
+int CHexMergeView::GetLength()
+{
+ return m_pif->get_length();
+}
+
+/**
+ * @brief Checks if file has changed since last update
+ * @param [in] path File to check
+ * @return TRUE if file is changed.
+ */
+BOOL CHexMergeView::IsFileChangedOnDisk(LPCTSTR path)
+{
+ // NB: FileTimes are measured in 100 nanosecond intervals since 1601-01-01.
+ BOOL bChanged = FALSE;
+ HANDLE h = CreateFile(path, FILE_READ_ATTRIBUTES, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ UINT64 mtime = GetLastWriteTime(h);
+ UINT64 lower = min(mtime, m_mtime);
+ UINT64 upper = max(mtime, m_mtime);
+ BOOL bIgnoreSmallDiff = GetOptionsMgr()->GetBool(OPT_IGNORE_SMALL_FILETIME);
+ UINT64 tolerance = bIgnoreSmallDiff ? SmallTimeDiff * 10000000 : 0;
+ bChanged = upper - lower > tolerance || m_size != GetFileSize(h, 0);
+ CloseHandle(h);
+ }
+ return bChanged;
+}
+
+/**
+ * @brief Load file
+ */
+HRESULT CHexMergeView::LoadFile(LPCTSTR path)
+{
+ HANDLE h = CreateFile(path, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ HRESULT hr = SE(h != INVALID_HANDLE_VALUE);
+ if (hr != S_OK)
+ return hr;
+ m_mtime = GetLastWriteTime(h);
+ DWORD length = m_size = GetFileSize(h, 0);
+ hr = SE(length != INVALID_FILE_SIZE);
+ if (hr == S_OK)
+ {
+ if (void *buffer = GetBuffer(length))
+ {
+ DWORD cb = 0;
+ hr = SE(ReadFile(h, buffer, length, &cb, 0) && cb == length);
+ if (hr != S_OK)
+ GetBuffer(0);
+ }
+ else
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ }
+ CloseHandle(h);
+ return hr;
+}
+
+/**
+ * @brief Save file
+ */
+HRESULT CHexMergeView::SaveFile(LPCTSTR path)
+{
+ // Warn user in case file has been changed by someone else
+ if (IsFileChangedOnDisk(path))
+ {
+ CString msg;
+ LangFormatString1(msg, IDS_FILECHANGED_ONDISK, path);
+ if (AfxMessageBox(msg, MB_ICONWARNING | MB_YESNO) == IDNO)
+ return S_OK;
+ }
+ // Ask user what to do about FILE_ATTRIBUTE_READONLY
+ CString strPath = path;
+ BOOL bApplyToAll = FALSE;
+ if (GetMainFrame()->HandleReadonlySave(strPath, FALSE, bApplyToAll) == IDCANCEL)
+ return S_OK;
+ path = strPath;
+ // Take a chance to create a backup
+ if (!GetMainFrame()->CreateBackup(FALSE, path))
+ return S_OK;
+ // Write data to an intermediate file
+ String tempPath = env_GetTempPath(0);
+ String sIntermediateFilename = env_GetTempFileName(tempPath.c_str(), _T("MRG_"), 0);
+ if (sIntermediateFilename.empty())
+ return E_FAIL; //Nothing to do if even tempfile name fails
+ HANDLE h = CreateFile(sIntermediateFilename.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
+ 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ HRESULT hr = SE(h != INVALID_HANDLE_VALUE);
+ if (hr != S_OK)
+ return hr;
+ DWORD length = GetLength();
+ void *buffer = GetBuffer(length);
+ if (buffer == 0)
+ return E_POINTER;
+ DWORD cb = 0;
+ hr = SE(WriteFile(h, buffer, length, &cb, 0) && cb == length);
+ UINT64 mtime = GetLastWriteTime(h);
+ CloseHandle(h);
+ if (hr != S_OK)
+ return hr;
+ hr = SE(CopyFile(sIntermediateFilename.c_str(), path, FALSE));
+ if (hr != S_OK)
+ return hr;
+ m_mtime = mtime;
+ SetModified(FALSE);
+ hr = SE(DeleteFile(sIntermediateFilename.c_str()));
+ if (hr != S_OK)
+ {
+ LogErrorString(Fmt(_T("DeleteFile(%s) failed: %s"),
+ sIntermediateFilename.c_str(), GetSysError(hr)));
+ }
+ return S_OK;
+}
+
+/**
+ * @brief Get status
+ */
+IHexEditorWindow::Status *CHexMergeView::GetStatus()
+{
+ return m_pif->get_status();
+}
+
+/**
+ * @brief Get modified flag
+ */
+BOOL CHexMergeView::GetModified()
+{
+ return m_pif->get_status()->iFileChanged;
+}
+
+/**
+ * @brief Set modified flag
+ */
+void CHexMergeView::SetModified(BOOL bModified)
+{
+ m_pif->get_status()->iFileChanged = bModified;
+}
+
+/**
+ * @brief Get readonly flag
+ */
+BOOL CHexMergeView::GetReadOnly()
+{
+ return m_pif->get_settings()->bReadOnly;
+}
+
+/**
+ * @brief Set readonly flag
+ */
+void CHexMergeView::SetReadOnly(BOOL bReadOnly)
+{
+ m_pif->get_settings()->bReadOnly = bReadOnly;
+}
+
+/**
+ * @brief Allow the control to update all kinds of things that need to be updated when
+ * the window or content buffer have been resized or certain settings have been changed.
+ */
+void CHexMergeView::ResizeWindow()
+{
+ m_pif->resize_window();
+}
+
+/**
+ * @brief Repaint a range of bytes
+ */
+void CHexMergeView::RepaintRange(int i, int j)
+{
+ int iBytesPerLine = m_pif->get_settings()->iBytesPerLine;
+ m_pif->repaint(i / iBytesPerLine, j / iBytesPerLine);
+}
+
+/**
+ * @brief Find a sequence of bytes
+ */
+void CHexMergeView::OnEditFind()
+{
+ m_pif->CMD_find();
+}
+
+/**
+ * @brief Find & replace a sequence of bytes
+ */
+void CHexMergeView::OnEditReplace()
+{
+ m_pif->CMD_replace();
+}
+
+/**
+ * @brief Repeat last find in one or another direction
+ */
+void CHexMergeView::OnEditRepeat()
+{
+ if (GetKeyState(VK_SHIFT) < 0)
+ m_pif->CMD_findprev();
+ else
+ m_pif->CMD_findnext();
+}
+
+/**
+ * @brief Cut selected content
+ */
+void CHexMergeView::OnEditCut()
+{
+ m_pif->CMD_edit_cut();
+}
+
+/**
+ * @brief Copy selected content
+ */
+void CHexMergeView::OnEditCopy()
+{
+ m_pif->CMD_edit_copy();
+}
+
+/**
+ * @brief Paste clipboard content over selected content
+ */
+void CHexMergeView::OnEditPaste()
+{
+ m_pif->CMD_edit_paste();
+}
+
+/**
+ * @brief Clear selected content
+ */
+void CHexMergeView::OnEditClear()
+{
+ m_pif->CMD_edit_clear();
+}
+
+/**
+ * @brief Check for keyboard commands
+ */
+BOOL CHexMergeView::PreTranslateMessage(MSG* pMsg)
+{
+ if (GetTopLevelFrame()->PreTranslateMessage(pMsg))
+ return TRUE;
+ if (pMsg->message == WM_KEYDOWN)
+ {
+ // Close window in response to VK_ESCAPE if user has allowed it from options
+ if (pMsg->wParam == VK_ESCAPE && GetOptionsMgr()->GetBool(OPT_CLOSE_WITH_ESC))
+ {
+ GetParentFrame()->Po...
[truncated message content] |