1 /////////////////////////////////////////////////////////////////////////////
2 // WinMerge: an interactive diff/merge utility
3 // Copyright (C) 1997 Dean P. Grimm
4 // SPDX-License-Identifier: GPL-2.0-or-later
5 /////////////////////////////////////////////////////////////////////////////
9 * @brief Declaration of CMergeDoc class
13 #include "DiffTextBuffer.h"
18 #include "DiffWrapper.h"
21 #include "PathContext.h"
22 #include "IMergeDoc.h"
25 * @brief Additional action codes for WinMerge.
26 * @note Reserve first 100 for CrystalEditor
30 CE_ACTION_MERGE = 100, /**< Merging action */
34 * @brief Return statuses of file rescan
38 RESCAN_OK = 0, /**< Rescan succeeded */
39 RESCAN_SUPPRESSED, /**< Rescan not done - suppressed */
40 RESCAN_FILE_ERR, /**< Error reading file */
41 RESCAN_TEMP_ERR, /**< Error saving to temp file */
45 * @brief File saving statuses
49 SAVE_DONE = 0, /**< Saving succeeded */
50 SAVE_FAILED, /**< Saving failed */
51 SAVE_PACK_FAILED, /**< Plugin failed to pack file */
52 SAVE_NO_FILENAME, /**< File has no filename */
53 SAVE_CANCELLED, /**< Saving was cancelled */
57 * @brief Types for buffer. Buffer's type defines behavior
58 * of buffer when saving etc.
60 * Difference between BUFFERTYPE::NORMAL and BUFFERTYPE::NORMAL_NAMED is
61 * that _NAMED has description text given and which is shown
62 * instead of filename.
64 * BUFFERTYPE::UNNAMED is created empty buffer (scratchpad), it has
65 * no filename, and default description is given for it. After
66 * this buffer is saved it becomes _SAVED. It is not equal to
67 * NORMAL_NAMED, since scratchpads don't have plugins etc.
71 NORMAL = 0, /**< Normal, file loaded from disk */
72 NORMAL_NAMED, /**< Normal, description given */
73 UNNAMED, /**< Empty, created buffer */
74 UNNAMED_SAVED, /**< Empty buffer saved with filename */
78 std::array<int, 3> begin; // 0-based, eg, begin[0] is from str1
79 std::array<int, 3> end; // 0-based, eg, end[1] is from str2
80 std::array<int, 3> beginline;
81 std::array<int, 3> endline;
84 WordDiff(int s1=0, int e1=0, int bl1=0, int el1=0, int s2=0, int e2=0, int bl2=0, int el2=0, int s3=0, int e3=0, int bl3=0, int el3=0, int op=0)
86 , beginline{bl1, bl2, bl3}
87 , endline{el1, el2, el3}
97 WordDiff(const WordDiff & src)
100 , beginline{src.beginline}
101 , endline{src.endline}
107 struct CurrentWordDiff
117 class CMergeEditView;
119 class PrediffingInfo;
120 class CMergeEditFrame;
122 class CEncodingErrorBar;
126 * @brief Document class for merging two files
128 class CMergeDoc : public CDocument, public IMergeDoc
133 static int m_nBuffersTemp;
135 // Begin declaration of CMergeDoc
137 std::unique_ptr<CDiffTextBuffer> m_ptBuf[3]; /**< Left/Middle/Right side text buffer */
141 protected: // create from serialization only
143 DECLARE_DYNCREATE(CMergeDoc)
147 std::unique_ptr<DiffFileInfo> m_pSaveFileInfo[3];
148 std::unique_ptr<DiffFileInfo> m_pRescanFileInfo[3];
150 UINT m_nTrivialDiffs; /**< Amount of trivial (ignored) diffs */
151 PathContext m_filePaths; /**< Filepaths for this document */
152 /// String of concatenated filenames as text to apply plugins filter to
153 String m_strBothFilenames;
155 CMergeEditView * GetActiveMergeView();
156 CMergeEditView * GetActiveMergeGroupView(int nBuffer);
157 void UpdateHeaderPath(int pane);
158 void UpdateHeaderActivity(int pane, bool bActivate);
159 void RefreshOptions();
160 void UpdateResources();
161 bool OpenDocs(int nFiles, const FileLocation fileloc[],
162 const bool bRO[], const String strDesc[]);
163 int LoadFile(CString sFileName, int nBuffer, bool & readOnly, const FileTextEncoding & encoding);
164 void MoveOnLoad(int nPane = -1, int nLinIndex = -1);
165 void ChangeFile(int nBuffer, const String& path, int nLineIndex = -1);
166 void RescanIfNeeded(float timeOutInSecond);
167 int Rescan(bool &bBinary, IDENTLEVEL &identical, bool bForced = false);
168 void CheckFileChanged(void) override;
169 int ShowMessageBox(const String& sText, unsigned nType = MB_OK, unsigned nIDHelp = 0);
170 void ShowRescanError(int nRescanResult, IDENTLEVEL identical);
172 void CopyAllList(int srcPane, int dstPane);
173 void CopyMultipleList(int srcPane, int dstPane, int firstDiff, int lastDiff, int firstWordDiff = -1, int lastWordDiff = -1);
174 void DoAutoMerge(int dstPane);
175 bool SanityCheckDiff(DIFFRANGE dr) const;
176 bool WordListCopy(int srcPane, int dstPane, int nDiff, int nFirstWordDiff, int nLastWordDiff, const std::vector<int> *pWordDiffIndice, bool bGroupWithPrevious = false, bool bUpdateView = true);
177 bool ListCopy(int srcPane, int dstPane, int nDiff = -1, bool bGroupWithPrevious = false, bool bUpdateView = true);
178 bool TrySaveAs(String& strPath, int &nLastErrorCode, String & sError,
179 int nBuffer, PackingInfo * pInfoTempUnpacker);
180 bool DoSave(LPCTSTR szPath, bool &bSaveSuccess, int nBuffer);
181 bool DoSaveAs(LPCTSTR szPath, bool &bSaveSuccess, int nBuffer);
182 int RightLineInMovedBlock(int pane, int line);
183 int LeftLineInMovedBlock(int pane, int line);
184 void SetEditedAfterRescan(int nBuffer);
185 bool IsEditedAfterRescan(int nBuffer = -1) const;
187 void SetUnpacker(const PackingInfo * infoUnpacker);
188 void SetPrediffer(const PrediffingInfo * infoPrediffer);
189 void GetPrediffer(PrediffingInfo * infoPrediffer);
190 void AddMergeViews(CMergeEditView * pView[3]);
191 void RemoveMergeViews(int nGroup);
192 void SetLocationView(CLocationView *pLocationView) { m_pLocationView = pLocationView; }
194 void SetDirDoc(CDirDoc * pDirDoc) override;
195 CDirDoc * GetDirDoc() const { return m_pDirDoc; }
196 void DirDocClosing(CDirDoc * pDirDoc) override;
197 bool CloseNow() override;
198 void SwapFiles(int nFromIndex, int nToIndex);
200 CMergeEditView * GetView(int group, int buffer) const { return m_pView[group][buffer]; }
201 CLocationView * GetLocationView() { return m_pLocationView; }
202 std::vector<CMergeEditView *> GetViewList(int nGroup = -1, int nBuffer = -1) const {
203 std::vector<CMergeEditView *> list;
205 for (int nBuffer2 = 0; nBuffer2 < m_nBuffers; ++nBuffer2)
206 list.push_back(m_pView[nGroup][nBuffer2]);
207 else if (nBuffer != -1)
208 for (int nGroup2 = 0; nGroup2 < m_nGroups; ++nGroup2)
209 list.push_back(m_pView[nGroup2][nBuffer]);
212 for (int nGroup3 = 0; nGroup3 < m_nGroups; nGroup3++)
213 for (int nBuffer3 = 0; nBuffer3 < m_nBuffers; ++nBuffer3)
214 list.push_back(m_pView[nGroup3][nBuffer3]);
218 template <typename Function>
219 void ForEachView(int nBuffer, Function func) {
220 for (int nGroup = 0; nGroup < m_nGroups; nGroup++)
221 func(m_pView[nGroup][nBuffer]);
223 template <typename Function>
224 void ForEachView(Function func) {
225 for (int nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
227 for (int nGroup = 0; nGroup < m_nGroups; nGroup++)
228 func(m_pView[nGroup][nBuffer]);
231 template <typename Function>
232 void ForEachActiveGroupView(Function func) {
233 int nGroup = GetActiveMergeView()->m_nThisGroup;
234 for (int nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
235 func(m_pView[nGroup][nBuffer]);
237 CMergeEditFrame * GetParentFrame();
240 bool DeleteSyncPoint(int pane, int nLine, bool bRescan = true);
241 void ClearSyncPoints();
242 bool HasSyncPoints();
243 std::vector<std::vector<int> > GetSyncPointList();
244 String GetDescription(int pane) const { return m_strDesc[pane]; }
247 // ClassWizard generated virtual function overrides
248 //{{AFX_VIRTUAL(CMergeDoc)
250 virtual BOOL OnNewDocument();
251 virtual void Serialize(CArchive& ar);
252 virtual BOOL SaveModified();
253 virtual void DeleteContents ();
254 virtual void SetTitle(LPCTSTR lpszTitle);
257 // Implementation in MergeDocLineDiffs.cpp
259 typedef enum { BYTEDIFF, WORDDIFF } DIFFLEVEL;
260 void Showlinediff(CMergeEditView *pView, bool bReversed = false);
261 void AddToSubstitutionFilters(CMergeEditView* pView, bool bReversed = false);
262 std::vector<WordDiff> GetWordDiffArrayInDiffBlock(int nDiff);
263 std::vector<WordDiff> GetWordDiffArray(int nLineIndex);
264 void ClearWordDiffCache(int nDiff = -1);
266 void Computelinediff(CMergeEditView *pView, CRect rc[], bool bReversed);
267 std::map<int, std::vector<WordDiff> > m_cacheWordDiffs;
268 // End MergeDocLineDiffs.cpp
270 // Implementation in MergeDocEncoding.cpp
272 bool DoFileEncodingDialog();
273 // End MergeDocEncoding.cpp
277 FileChange IsFileChangedOnDisk(LPCTSTR szPath, DiffFileInfo &dfi,
278 bool bSave, int nBuffer);
279 bool PromptAndSaveIfNeeded(bool bAllowCancel);
280 std::vector<int> undoTgt;
281 std::vector<int>::iterator curUndo;
282 void FlushAndRescan(bool bForced = false);
283 void SetCurrentDiff(int nDiff);
284 int GetCurrentDiff() const { return m_nCurDiff; }
285 const CurrentWordDiff& GetCurrentWordDiff() const { return m_CurWordDiff; }
286 bool EqualCurrentWordDiff(int nBuffer, const CPoint& ptStart, const CPoint& ptEnd) const
288 return (m_CurWordDiff.nPane == nBuffer && m_CurWordDiff.ptStart == ptStart && m_CurWordDiff.ptEnd == ptEnd);
290 virtual ~CMergeDoc();
291 void SetDetectMovedBlocks(bool bDetectMovedBlocks);
292 bool IsMixedEOL(int nBuffer) const;
293 bool OpenWithUnpackerDialog();
294 bool GenerateReport(const String& sFileName) const override;
295 void SetAutoMerged(bool bAutoMerged) { m_bAutoMerged = bAutoMerged; }
296 bool GetAutoMerged() const { return m_bAutoMerged; };
297 bool IsModified() const
299 for (int nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
300 if (m_ptBuf[nBuffer]->IsModified())
306 for (int nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
307 if (m_ptBuf[nBuffer]->CanUndo())
312 // implementation methods
314 bool GetBreakType() const;
315 bool GetByteColoringOption() const;
316 bool IsValidCodepageForMergeEditor(unsigned cp) const;
317 void SanityCheckCodepage(FileLocation & fileinfo);
318 DWORD LoadOneFile(int index, String filename, bool readOnly, const String& strDesc, const FileTextEncoding & encoding);
319 void SetTableProperties();
321 // Implementation data
323 int m_nCurDiff; /**< Selected diff, 0-based index, -1 if no diff selected */
324 CurrentWordDiff m_CurWordDiff;
325 CMergeEditView * m_pView[3][3]; /**< Pointer to left/middle/right view */
326 CLocationView * m_pLocationView; /**< Pointer to locationview */
328 bool m_bEnableRescan; /**< Automatic rescan enabled/disabled */
329 COleDateTime m_LastRescan; /**< Time of last rescan (for delaying) */
330 CDiffWrapper m_diffWrapper;
331 /// information about the file packer/unpacker
332 std::unique_ptr<PackingInfo> m_pInfoUnpacker;
333 String m_strDesc[3]; /**< Left/Middle/Right side description text */
334 BUFFERTYPE m_nBufferType[3];
335 bool m_bEditAfterRescan[3]; /**< Left/middle/right doc edited after rescanning */
336 TempFile m_tempFiles[3]; /**< Temp files for compared files */
338 bool m_bInvertDiffContext;
339 bool m_bMixedEol; /**< Does this document have mixed EOL style? */
340 std::unique_ptr<CEncodingErrorBar> m_pEncodingErrorBar;
341 bool m_bHasSyncPoints;
343 std::optional<bool> m_bEnableTableEditing;
345 friend class RescanSuppress;
348 // Generated message map functions
350 //{{AFX_MSG(CMergeDoc)
351 afx_msg void OnFileSave();
352 afx_msg void OnFileSaveLeft();
353 afx_msg void OnFileSaveMiddle();
354 afx_msg void OnFileSaveRight();
355 afx_msg void OnFileSaveAsLeft();
356 afx_msg void OnUpdateFileSaveAsMiddle(CCmdUI* pCmdUI);
357 afx_msg void OnFileSaveAsMiddle();
358 afx_msg void OnFileSaveAsRight();
359 afx_msg void OnUpdateStatusNum(CCmdUI* pCmdUI);
360 afx_msg void OnUpdatePluginName(CCmdUI* pCmdUI);
361 afx_msg void OnFileReload();
362 afx_msg void OnFileEncoding();
363 afx_msg void OnDiffContext(UINT nID);
364 afx_msg void OnUpdateDiffContext(CCmdUI* pCmdUI);
365 afx_msg void OnToolsGenerateReport();
366 afx_msg void OnToolsGeneratePatch();
367 afx_msg void OnCtxtOpenWithUnpacker();
368 afx_msg void OnBnClickedFileEncoding();
369 afx_msg void OnBnClickedPlugin();
370 afx_msg void OnBnClickedHexView();
372 afx_msg void OnFileRecompareAsText();
373 afx_msg void OnFileRecompareAsTable();
374 afx_msg void OnFileRecompareAsXML();
375 afx_msg void OnUpdateFileRecompareAsText(CCmdUI* pCmdUI);
376 afx_msg void OnUpdateFileRecompareAsTable(CCmdUI* pCmdUI);
377 afx_msg void OnUpdateFileRecompareAsXML(CCmdUI* pCmdUI);
378 afx_msg void OnFileRecompareAs(UINT nID);
379 afx_msg void OnUpdateSwapContext(CCmdUI* pCmdUI);
381 DECLARE_MESSAGE_MAP()
383 void PrimeTextBuffers();
385 void AdjustDiffBlocks();
386 void AdjustDiffBlock(DiffMap & diffmap, const DIFFRANGE & diffrange, int lo0, int hi0, int lo1, int hi1);
387 int GetMatchCost(const String &Line0, const String &Line1);
388 void FlagTrivialLines();
389 void FlagMovedLines();
390 String GetFileExt(LPCTSTR sFileName, LPCTSTR sDescription) const;
391 void DoFileSave(int pane);
395 * @brief return true if there are synchronization points
397 inline bool CMergeDoc::HasSyncPoints()
399 return m_bHasSyncPoints;