OSDN Git Service

Improve plugin system (#797)
[winmerge-jp/winmerge-jp.git] / Src / MergeDoc.h
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 /////////////////////////////////////////////////////////////////////////////
6 /** 
7  * @file  MergeDoc.h
8  *
9  * @brief Declaration of CMergeDoc class
10  */
11 #pragma once
12
13 #include "DiffTextBuffer.h"
14 #include <vector>
15 #include <map>
16 #include <memory>
17 #include <optional>
18 #include "DiffWrapper.h"
19 #include "DiffList.h"
20 #include "TempFile.h"
21 #include "PathContext.h"
22 #include "IMergeDoc.h"
23
24 /**
25  * @brief Additional action codes for WinMerge.
26  * @note Reserve first 100 for CrystalEditor
27  */
28 enum
29 {
30         CE_ACTION_MERGE = 100, /**< Merging action */
31 };
32
33 /**
34  * @brief Return statuses of file rescan
35  */
36 enum
37 {
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 */
42 };
43
44 /**
45  * @brief File saving statuses
46  */
47 enum
48 {
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 */  
54 };
55
56 /**
57  * @brief Types for buffer. Buffer's type defines behavior
58  * of buffer when saving etc.
59  * 
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.
63  *
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.
68  */
69 enum class BUFFERTYPE
70 {
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 */
75 };
76
77 struct WordDiff {
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;
82         int op;
83
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)
85                 : begin{s1, s2, s3}
86                 , beginline{bl1, bl2, bl3}
87                 , endline{el1, el2, el3}
88                 , op(op)
89         {
90                 if (s1>e1) e1=s1;
91                 if (s2>e2) e2=s2;
92                 if (s3>e3) e3=s3;
93                 end[0] = e1;
94                 end[1] = e2;
95                 end[2] = e3;
96         }
97         WordDiff(const WordDiff & src)
98                 : begin{src.begin}
99                 , end{src.end}
100                 , beginline{src.beginline}
101                 , endline{src.endline}
102                 , op(src.op)
103         {
104         }
105 };
106
107 struct CurrentWordDiff
108 {
109         int nDiff;
110         size_t nWordDiff;
111         int nPane;
112         CPoint ptStart;
113         CPoint ptEnd;
114 };
115
116 struct DiffFileInfo;
117 class CMergeEditView;
118 class PackingInfo;
119 class PrediffingInfo;
120 class CMergeEditFrame;
121 class CDirDoc;
122 class CEncodingErrorBar;
123 class CLocationView;
124
125 /**
126  * @brief Document class for merging two files
127  */
128 class CMergeDoc : public CDocument, public IMergeDoc
129 {
130 public:
131 // Attributes
132 public:
133         static int m_nBuffersTemp;
134
135 // Begin declaration of CMergeDoc
136
137         std::unique_ptr<CDiffTextBuffer> m_ptBuf[3]; /**< Left/Middle/Right side text buffer */
138         int m_nBuffers;
139         int m_nGroups;
140
141 protected: // create from serialization only
142         CMergeDoc();
143         DECLARE_DYNCREATE(CMergeDoc)
144
145         // Operations
146 public: 
147         std::unique_ptr<DiffFileInfo> m_pSaveFileInfo[3];
148         std::unique_ptr<DiffFileInfo> m_pRescanFileInfo[3];
149         DiffList m_diffList;
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;
154
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);
171         bool Undo();
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 CopyMultiplePartialList(int srcPane, int dstPane, int firstDiff, int lastDiff, int firstLineDiff = -1, int lastLineDiff = -1);
175         void DoAutoMerge(int dstPane);
176         bool SanityCheckDiff(DIFFRANGE dr) const;
177         bool WordListCopy(int srcPane, int dstPane, int nDiff, int nFirstWordDiff, int nLastWordDiff, const std::vector<int> *pWordDiffIndice, bool bGroupWithPrevious = false, bool bUpdateView = true);
178         bool PartialListCopy(int srcPane, int dstPane, int nDiff, int firstLine, int lastLine = -1, bool bGroupWithPrevious = false, bool bUpdateView = true);
179         bool ListCopy(int srcPane, int dstPane, int nDiff = -1, bool bGroupWithPrevious = false, bool bUpdateView = true);
180         bool TrySaveAs(String& strPath, int &nLastErrorCode, String & sError,
181                 int nBuffer, PackingInfo& infoTempUnpacker);
182         bool DoSave(LPCTSTR szPath, bool &bSaveSuccess, int nBuffer);
183         bool DoSaveAs(LPCTSTR szPath, bool &bSaveSuccess, int nBuffer);
184         int RightLineInMovedBlock(int pane, int line);
185         int LeftLineInMovedBlock(int pane, int line);
186         void SetEditedAfterRescan(int nBuffer);
187         bool IsEditedAfterRescan(int nBuffer = -1) const;
188
189         const PackingInfo* GetUnpacker() const override { return &m_infoUnpacker; }
190         void SetUnpacker(const PackingInfo * infoUnpacker);
191         void SetPrediffer(const PrediffingInfo * infoPrediffer);
192         void GetPrediffer(PrediffingInfo * infoPrediffer);
193         const PrediffingInfo *GetPrediffer() const override;
194         void AddMergeViews(CMergeEditView * pView[3]);
195         void RemoveMergeViews(int nGroup);
196         void SetLocationView(CLocationView *pLocationView) { m_pLocationView = pLocationView; }
197
198         CDirDoc * GetDirDoc() const override { return m_pDirDoc; }
199         void SetDirDoc(CDirDoc * pDirDoc) override;
200         void DirDocClosing(CDirDoc * pDirDoc) override;
201         bool CloseNow() override;
202         int GetFileCount() const override { return m_filePaths.GetSize(); }
203         String GetPath(int pane) const override { return m_filePaths[pane]; } 
204         bool GetReadOnly(int pane) const override { return m_ptBuf[pane]->m_bReadOnly; }
205         void SwapFiles(int nFromIndex, int nToIndex);
206
207         CMergeEditView * GetView(int group, int buffer) const { return m_pView[group][buffer]; }
208         CLocationView * GetLocationView() { return m_pLocationView; }
209         std::vector<CMergeEditView *> GetViewList(int nGroup = -1, int nBuffer = -1) const {
210                 std::vector<CMergeEditView *> list;
211                 if (nGroup != -1)
212                         for (int nBuffer2 = 0; nBuffer2 < m_nBuffers; ++nBuffer2)
213                                 list.push_back(m_pView[nGroup][nBuffer2]);
214                 else if (nBuffer != -1)
215                         for (int nGroup2 = 0; nGroup2 < m_nGroups; ++nGroup2)
216                                 list.push_back(m_pView[nGroup2][nBuffer]);
217                 else
218                 {
219                         for (int nGroup3 = 0; nGroup3 < m_nGroups; nGroup3++)
220                                 for (int nBuffer3 = 0; nBuffer3 < m_nBuffers; ++nBuffer3)
221                                         list.push_back(m_pView[nGroup3][nBuffer3]);
222                 }
223                 return list;
224         }
225         template <typename Function>
226         void ForEachView(int nBuffer, Function func) {
227                 for (int nGroup = 0; nGroup < m_nGroups; nGroup++)
228                         func(m_pView[nGroup][nBuffer]);
229         }
230         template <typename Function>
231         void ForEachView(Function func) {
232                 for (int nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
233                 {
234                         for (int nGroup = 0; nGroup < m_nGroups; nGroup++)
235                                 func(m_pView[nGroup][nBuffer]);
236                 }
237         }
238         template <typename Function>
239         void ForEachActiveGroupView(Function func) {
240                 int nGroup = GetActiveMergeView()->m_nThisGroup;
241                 for (int nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
242                         func(m_pView[nGroup][nBuffer]);
243         }
244         CMergeEditFrame * GetParentFrame();
245
246         void AddSyncPoint();
247         bool DeleteSyncPoint(int pane, int nLine, bool bRescan = true);
248         void ClearSyncPoints();
249         bool HasSyncPoints();
250         std::vector<std::vector<int> > GetSyncPointList();
251         String GetDescription(int pane) const { return m_strDesc[pane]; }
252
253         // Overrides
254         // ClassWizard generated virtual function overrides
255         //{{AFX_VIRTUAL(CMergeDoc)
256         public:
257         virtual BOOL OnNewDocument();
258         virtual void Serialize(CArchive& ar);
259         virtual BOOL SaveModified();
260         virtual void DeleteContents ();
261         virtual void SetTitle(LPCTSTR lpszTitle);
262         //}}AFX_VIRTUAL
263
264 // Implementation in MergeDocLineDiffs.cpp
265 public:
266         typedef enum { BYTEDIFF, WORDDIFF } DIFFLEVEL;
267         void Showlinediff(CMergeEditView *pView, bool bReversed = false);
268         void AddToSubstitutionFilters(CMergeEditView* pView, bool bReversed = false);
269         std::vector<WordDiff> GetWordDiffArrayInDiffBlock(int nDiff);
270         std::vector<WordDiff> GetWordDiffArray(int nLineIndex);
271         void ClearWordDiffCache(int nDiff = -1);
272 private:
273         void Computelinediff(CMergeEditView *pView, CRect rc[], bool bReversed);
274         std::map<int, std::vector<WordDiff> > m_cacheWordDiffs;
275 // End MergeDocLineDiffs.cpp
276
277 // Implementation in MergeDocEncoding.cpp
278 public:
279         bool DoFileEncodingDialog();
280 // End MergeDocEncoding.cpp
281
282 // Implementation
283 public:
284         FileChange IsFileChangedOnDisk(LPCTSTR szPath, DiffFileInfo &dfi,
285                 bool bSave, int nBuffer);
286         bool PromptAndSaveIfNeeded(bool bAllowCancel);
287         std::vector<int> undoTgt;
288         std::vector<int>::iterator curUndo;
289         void FlushAndRescan(bool bForced = false);
290         void SetCurrentDiff(int nDiff);
291         int GetCurrentDiff() const { return m_nCurDiff; }
292         const CurrentWordDiff& GetCurrentWordDiff() const { return m_CurWordDiff; }
293         bool EqualCurrentWordDiff(int nBuffer, const CPoint& ptStart, const CPoint& ptEnd) const
294         {
295                 return (m_CurWordDiff.nPane == nBuffer && m_CurWordDiff.ptStart == ptStart && m_CurWordDiff.ptEnd == ptEnd);
296         }
297         virtual ~CMergeDoc();
298         void SetDetectMovedBlocks(bool bDetectMovedBlocks);
299         bool IsMixedEOL(int nBuffer) const;
300         bool GenerateReport(const String& sFileName) const override;
301         void SetAutoMerged(bool bAutoMerged) { m_bAutoMerged = bAutoMerged; }
302         bool GetAutoMerged() const { return m_bAutoMerged; };
303         bool IsModified() const
304         {
305                 for (int nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
306                         if (m_ptBuf[nBuffer]->IsModified())
307                                 return true;
308                 return false;
309         }
310         bool CanUndo() const
311         {
312                 for (int nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
313                         if (m_ptBuf[nBuffer]->CanUndo())
314                                 return true;
315                 return false;
316         }
317         std::optional<bool> GetEnableTableEditing() const { return m_bEnableTableEditing; }
318         void SetEnableTableEditing(std::optional<bool> bEnableTableEditing) { m_bEnableTableEditing = bEnableTableEditing; }
319         bool GetAutomaticRescan() const { return m_bAutomaticRescan; }
320
321 // implementation methods
322 private:
323         bool GetBreakType() const;
324         bool GetByteColoringOption() const;
325         bool IsValidCodepageForMergeEditor(unsigned cp) const;
326         void SanityCheckCodepage(FileLocation & fileinfo);
327         DWORD LoadOneFile(int index, String filename, bool readOnly, const String& strDesc, const FileTextEncoding & encoding);
328         void SetTableProperties();
329
330 // Implementation data
331 protected:
332         int m_nCurDiff; /**< Selected diff, 0-based index, -1 if no diff selected */
333         CurrentWordDiff m_CurWordDiff;
334         CMergeEditView * m_pView[3][3]; /**< Pointer to left/middle/right view */
335         CLocationView * m_pLocationView; /**< Pointer to locationview */
336         CDirDoc * m_pDirDoc;
337         bool m_bEnableRescan; /**< Automatic rescan enabled/disabled */
338         COleDateTime m_LastRescan; /**< Time of last rescan (for delaying) */ 
339         CDiffWrapper m_diffWrapper;
340         /// information about the file packer/unpacker
341         PackingInfo m_infoUnpacker;
342         String m_strDesc[3]; /**< Left/Middle/Right side description text */
343         BUFFERTYPE m_nBufferType[3];
344         bool m_bEditAfterRescan[3]; /**< Left/middle/right doc edited after rescanning */
345         TempFile m_tempFiles[3]; /**< Temp files for compared files */
346         int m_nDiffContext;
347         bool m_bInvertDiffContext;
348         bool m_bMixedEol; /**< Does this document have mixed EOL style? */
349         std::unique_ptr<CEncodingErrorBar> m_pEncodingErrorBar;
350         bool m_bHasSyncPoints;
351         bool m_bAutoMerged;
352         std::optional<bool> m_bEnableTableEditing;
353         /**
354          * Are automatic rescans enabled?
355          * If automatic rescans are enabled then we rescan files after edit
356          * events, unless timer suppresses rescan. We suppress rescans within
357          * certain time from previous rescan.
358          */
359         bool m_bAutomaticRescan;
360 // friend access
361         friend class RescanSuppress;
362
363
364 // Generated message map functions
365 protected:
366         //{{AFX_MSG(CMergeDoc)
367         afx_msg void OnFileSave();
368         afx_msg void OnFileSaveLeft();
369         afx_msg void OnFileSaveMiddle();
370         afx_msg void OnFileSaveRight();
371         afx_msg void OnFileSaveAsLeft();
372         afx_msg void OnUpdateFileSaveAsMiddle(CCmdUI* pCmdUI);
373         afx_msg void OnFileSaveAsMiddle();
374         afx_msg void OnFileSaveAsRight();
375         afx_msg void OnUpdateStatusNum(CCmdUI* pCmdUI);
376         afx_msg void OnFileReload();
377         afx_msg void OnFileEncoding();
378         afx_msg void OnDiffContext(UINT nID);
379         afx_msg void OnUpdateDiffContext(CCmdUI* pCmdUI);
380         afx_msg void OnToolsGenerateReport();
381         afx_msg void OnToolsGeneratePatch();
382         afx_msg void OnOpenWithUnpacker();
383         afx_msg void OnApplyPrediffer();
384         afx_msg void OnBnClickedFileEncoding();
385         afx_msg void OnBnClickedPlugin();
386         afx_msg void OnBnClickedHexView();
387         afx_msg void OnOK();
388         afx_msg void OnFileRecompareAsText();
389         afx_msg void OnFileRecompareAsTable();
390         afx_msg void OnUpdateFileRecompareAsText(CCmdUI* pCmdUI);
391         afx_msg void OnUpdateFileRecompareAsTable(CCmdUI* pCmdUI);
392         afx_msg void OnFileRecompareAs(UINT nID);
393         afx_msg void OnUpdateSwapContext(CCmdUI* pCmdUI);
394         //}}AFX_MSG
395         DECLARE_MESSAGE_MAP()
396 private:
397         void PrimeTextBuffers();
398         void HideLines();
399         void AdjustDiffBlocks();
400         void AdjustDiffBlock(DiffMap & diffmap, const DIFFRANGE & diffrange, int lo0, int hi0, int lo1, int hi1);
401         int GetMatchCost(const String &Line0, const String &Line1);
402         void FlagTrivialLines();
403         void FlagMovedLines();
404         String GetFileExt(LPCTSTR sFileName, LPCTSTR sDescription) const;
405         void DoFileSave(int pane);
406 };
407
408 /**
409  * @brief return true if there are synchronization points
410  */
411 inline bool CMergeDoc::HasSyncPoints()
412 {
413         return m_bHasSyncPoints;
414 }
415