OSDN Git Service

PATCH: [ 2001190 ] Split CDiffTextBuffer to own files
authorKimmo Varis <kimmov@gmail.com>
Mon, 23 Jun 2008 22:54:36 +0000 (22:54 +0000)
committerKimmo Varis <kimmov@gmail.com>
Mon, 23 Jun 2008 22:54:36 +0000 (22:54 +0000)
Src/DiffTextBuffer.cpp [new file with mode: 0644]
Src/DiffTextBuffer.h [new file with mode: 0644]
Src/Merge.vcproj
Src/MergeDoc.cpp
Src/MergeDoc.h
Src/MergeEditView.cpp

diff --git a/Src/DiffTextBuffer.cpp b/Src/DiffTextBuffer.cpp
new file mode 100644 (file)
index 0000000..a7573a3
--- /dev/null
@@ -0,0 +1,751 @@
+/** 
+ * @file  DiffTextBuffer.cpp
+ *
+ * @brief Implementation file for CDiffTextBuffer
+ *
+ */
+// ID line follows -- this is updated by SVN
+// $Id$
+
+#include "stdafx.h"
+#include "UniFile.h"
+#include "files.h"
+#include "cs2cs.h"
+#include "locality.h"
+#include "coretools.h"
+#include "Merge.h"
+#include "OptionsDef.h"
+#include "Environment.h"
+#include "MergeLineFlags.h"
+#include "MergeDoc.h"
+#include "FileTransform.h"
+#include "FileTextEncoding.h"
+#include "DiffTextBuffer.h"
+
+static int IsTextFileStylePure(const UniMemFile::txtstats & stats);
+static CString GetLineByteTimeReport(UINT lines, __int64 bytes,
+       const COleDateTime & start);
+static void EscapeControlChars(CString &s);
+static LPCTSTR getEol(const CString &str);
+static int GetTextFileStyle(const UniMemFile::txtstats & stats);
+
+/**
+ * @brief Examine statistics in textFileStats and tell if the file has only one EOL type
+ */
+static int IsTextFileStylePure(const UniMemFile::txtstats & stats)
+{
+       int nType = 0;
+       nType += (stats.ncrlfs > 0);
+       nType += (stats.ncrs > 0);
+       nType += (stats.nlfs > 0);
+       return (nType <= 1);
+}
+
+/**
+ * @brief Return a string giving #lines and #bytes and how much time elapsed.
+ * @param [in] lines Count of lines.
+ * @param [in] bytes Count of bytes.
+ * @param [in] start Time used.
+ * @return Formatted string.
+ */
+static CString GetLineByteTimeReport(UINT lines, __int64 bytes,
+       const COleDateTime & start)
+{
+       String sLines = locality::NumToLocaleStr((int)lines);
+       String sBytes = locality::NumToLocaleStr(bytes);
+       COleDateTimeSpan duration = COleDateTime::GetCurrentTime() - start;
+       String sMinutes = locality::NumToLocaleStr((int)duration.GetTotalMinutes());
+       CString str;
+       str.Format(_T("%s lines (%s byte) saved in %sm%02ds")
+               , sLines.c_str(), sBytes.c_str(), sMinutes.c_str()
+               , duration.GetSeconds()
+               );
+       return str;
+}
+
+/**
+ * @brief Escape control characters.
+ * @param [in,out] s Line of text excluding eol chars.
+ *
+ * @note Escape sequences follow the pattern
+ * (leadin character, high nibble, low nibble, leadout character).
+ * The leadin character is '\x0F'. The leadout character is a backslash.
+ */
+static void EscapeControlChars(CString &s)
+{
+       // Compute buffer length required for escaping
+       int n = s.GetLength();
+       LPCTSTR q = s;
+       int i = n;
+       while (i)
+       {
+               TCHAR c = q[--i];
+               // Is it a control character in the range 0..31 except TAB?
+               if (!(c & ~_T('\x1F')) && c != _T('\t'))
+               {
+                       n += 3; // Need 3 extra characters to escape
+               }
+       }
+       // Reallocate accordingly
+       i = s.GetLength();
+       LPTSTR p = s.GetBufferSetLength(n);
+       // Copy/translate characters starting at end of string
+       while (i)
+       {
+               TCHAR c = p[--i];
+               // Is it a control character in the range 0..31 except TAB?
+               if (!(c & ~_T('\x1F')) && c != _T('\t'))
+               {
+                       // Bitwise OR with 0x100 so _itot() will output 3 hex digits
+                       _itot(0x100 | c, p + n - 4, 16);
+                       // Replace terminating zero with leadout character
+                       p[n - 1] = _T('\\');
+                       // Prepare to replace 1st hex digit with leadin character
+                       c = _T('\x0F');
+                       n -= 3;
+               }
+               p[--n] = c;
+       }
+}
+
+/// Return pointer to the eol chars of this string, or pointer to empty string if none
+static LPCTSTR getEol(const CString &str)
+{
+       if (str.GetLength()>1 && str[str.GetLength()-2]=='\r' && str[str.GetLength()-1]=='\n')
+               return (LPCTSTR)str + str.GetLength()-2;
+       if (str.GetLength()>0 && (str[str.GetLength()-1]=='\r' || str[str.GetLength()-1]=='\n'))
+               return (LPCTSTR)str + str.GetLength()-1;
+       return _T("");
+}
+
+/**
+ * @brief Examine statistics in textFileStats and return a crystaltextbuffer enum value for line style
+ */
+static int GetTextFileStyle(const UniMemFile::txtstats & stats)
+{
+       if (stats.ncrlfs >= stats.nlfs)
+       {
+               if (stats.ncrlfs >= stats.ncrs)
+               {
+                       return CRLF_STYLE_DOS;
+               }
+               else
+               {
+                       return CRLF_STYLE_MAC;
+               }
+       }
+       else
+       {
+               if (stats.nlfs >= stats.ncrs)
+               {
+                       return CRLF_STYLE_UNIX;
+               }
+               else
+               {
+                       return CRLF_STYLE_MAC;
+               }
+       }
+}
+
+CDiffTextBuffer::CDiffTextBuffer(CMergeDoc * pDoc, int pane)
+: m_pOwnerDoc(pDoc)
+, m_nThisPane(pane)
+, unpackerSubcode(0)
+{
+}
+
+BOOL CDiffTextBuffer::GetLine(int nLineIndex, CString &strLine)
+{ 
+       int nLineLength = CCrystalTextBuffer::GetLineLength 
+               ( nLineIndex ); 
+       
+       if( nLineLength < 0 ) 
+               return FALSE; 
+       else if( nLineLength == 0 ) 
+               strLine.Empty(); 
+       else 
+       { 
+               _tcsncpy ( strLine.GetBuffer( nLineLength + 1 ), 
+                       CCrystalTextBuffer::GetLineChars( nLineIndex ), 
+                       nLineLength ); 
+               strLine.ReleaseBuffer( nLineLength ); 
+       } 
+       return TRUE; 
+}
+
+void CDiffTextBuffer::SetModified(BOOL bModified /*= TRUE*/)
+{
+       CCrystalTextBuffer::SetModified (bModified);
+       m_pOwnerDoc->SetModifiedFlag (bModified);
+}
+
+BOOL CDiffTextBuffer::GetFullLine(int nLineIndex, CString &strLine)
+{
+       int cchText = GetFullLineLength(nLineIndex);
+       if (cchText == 0)
+               return FALSE;
+       LPTSTR pchText = strLine.GetBufferSetLength(cchText);
+       memcpy(pchText, GetLineChars(nLineIndex), cchText * sizeof(TCHAR));
+       return TRUE;
+}
+
+void CDiffTextBuffer::AddUndoRecord(BOOL bInsert, const CPoint & ptStartPos, const CPoint & ptEndPos, LPCTSTR pszText, int cchText, int nLinesToValidate, int nActionType /*= CE_ACTION_UNKNOWN*/, CDWordArray *paSavedRevisonNumbers)
+{
+       CGhostTextBuffer::AddUndoRecord(bInsert, ptStartPos, ptEndPos, pszText, cchText, nLinesToValidate, nActionType, paSavedRevisonNumbers);
+       if (m_aUndoBuf[m_nUndoPosition - 1].m_dwFlags & UNDO_BEGINGROUP)
+       {
+               m_pOwnerDoc->undoTgt.erase(m_pOwnerDoc->curUndo, m_pOwnerDoc->undoTgt.end());
+               m_pOwnerDoc->undoTgt.push_back(m_pOwnerDoc->GetView(m_nThisPane));
+               m_pOwnerDoc->curUndo = m_pOwnerDoc->undoTgt.end();
+       }
+}
+/**
+ * @brief Checks if a flag is set for line.
+ * @param [in] line Index (0-based) for line.
+ * @param [in] flag Flag to check.
+ * @return TRUE if flag is set, FALSE otherwise.
+ */
+BOOL CDiffTextBuffer::FlagIsSet(UINT line, DWORD flag)
+{
+       return ((m_aLines[line].m_dwFlags & flag) == flag);
+}
+
+
+/**
+Remove blank lines and clear winmerge flags
+(2003-06-21, Perry: I don't understand why this is necessary, but if this isn't 
+done, more and more gray lines appear in the file)
+(2003-07-31, Laoran I don't understand either why it is necessary, but it works
+fine, so let's go on with it)
+*/
+void CDiffTextBuffer::prepareForRescan()
+{
+       RemoveAllGhostLines();
+       for(int ct=GetLineCount()-1; ct>=0; --ct)
+       {
+               SetLineFlag(ct, LF_DIFF, FALSE, FALSE, FALSE);
+               SetLineFlag(ct, LF_TRIVIAL, FALSE, FALSE, FALSE);
+               SetLineFlag(ct, LF_MOVED, FALSE, FALSE, FALSE);
+       }
+}
+
+
+void CDiffTextBuffer::OnNotifyLineHasBeenEdited(int nLine)
+{
+       SetLineFlag(nLine, LF_DIFF, FALSE, FALSE, FALSE);
+       SetLineFlag(nLine, LF_TRIVIAL, FALSE, FALSE, FALSE);
+       SetLineFlag(nLine, LF_MOVED, FALSE, FALSE, FALSE);
+       CGhostTextBuffer::OnNotifyLineHasBeenEdited(nLine);
+}
+
+/**
+ * @brief Helper to determine the most used CRLF mode in the file
+ * Normal call : determine the CRLF mode of the current line
+ * Final call  : parameter lpLineBegin = NULL, return the style of the file
+ *
+ * @note  The lowest CRL_STYLE has the priority in case of equality
+ */
+int CDiffTextBuffer::NoteCRLFStyleFromBuffer(TCHAR *lpLineBegin, DWORD dwLineLen /* =0 */)
+{
+       static int count[3] = {0};
+       int iStyle = -1;
+
+       // give back the result when we ask for it
+       if (lpLineBegin == NULL)
+       {
+               iStyle = 0;
+               if (count[1] > count[iStyle])
+                       iStyle = 1;
+               if (count[2] > count[iStyle])
+                       iStyle = 2;
+
+               // reset the counter for the next file
+               count[0] = count[1] = count[2] = 0;
+
+               return iStyle;
+       }
+
+       if (dwLineLen >= 1)
+       {
+               if (lpLineBegin[dwLineLen-1] == _T('\r'))
+                       iStyle = CRLF_STYLE_MAC;
+               if (lpLineBegin[dwLineLen-1] == _T('\n'))
+                       iStyle = CRLF_STYLE_UNIX;
+       }
+       if (dwLineLen >= 2)
+       {
+               if (lpLineBegin[dwLineLen-2] == _T('\r') && lpLineBegin[dwLineLen-1] == _T('\n'))
+                       iStyle = CRLF_STYLE_DOS;
+       }
+       ASSERT (iStyle != -1);
+       count[iStyle] ++;
+       return iStyle;
+}
+
+/// Reads one line from filebuffer and inserts to textbuffer
+void CDiffTextBuffer::ReadLineFromBuffer(TCHAR *lpLineBegin, DWORD dwLineNum, DWORD dwLineLen /* =0 */)
+{
+       if (m_nSourceEncoding >= 0)
+               iconvert (lpLineBegin, m_nSourceEncoding, 1, m_nSourceEncoding == 15);
+       AppendLine(dwLineNum, lpLineBegin, dwLineLen);
+}
+
+/// Sets path for temporary files
+void CDiffTextBuffer::SetTempPath(String path)
+{
+       m_strTempPath = path;
+}
+
+bool CDiffTextBuffer::IsInitialized() const
+{
+       return !!m_bInit;
+}
+
+/**
+ * @brief Load file from disk into buffer
+ *
+ * @param [in] pszFileNameInit File to load
+ * @param [in] infoUnpacker Unpacker plugin
+ * @param [in] sToFindUnpacker String for finding unpacker plugin
+ * @param [out] readOnly Loading was lossy so file should be read-only
+ * @param [in] nCrlfStyle EOL style used
+ * @param [in] encoding Encoding used
+ * @param [out] sError Error message returned
+ * @return FRESULT_OK when loading succeed or (list in files.h):
+ * - FRESULT_OK_IMPURE : load OK, but the EOL are of different types
+ * - FRESULT_ERROR_UNPACK : plugin failed to unpack
+ * - FRESULT_ERROR : loading failed, sError contains error message
+ * - FRESULT_BINARY : file is binary file
+ * @note If this method fails, it calls InitNew so the CDiffTextBuffer is in a valid state
+ */
+int CDiffTextBuffer::LoadFromFile(LPCTSTR pszFileNameInit,
+               PackingInfo * infoUnpacker, LPCTSTR sToFindUnpacker, BOOL & readOnly,
+               int nCrlfStyle, const FileTextEncoding & encoding, CString &sError)
+{
+       ASSERT(!m_bInit);
+       ASSERT(m_aLines.GetSize() == 0);
+
+       // Unpacking the file here, save the result in a temporary file
+       String sFileName = pszFileNameInit;
+       if (infoUnpacker->bToBeScanned)
+       {
+               if (!FileTransform_Unpacking(sFileName, sToFindUnpacker, infoUnpacker, &unpackerSubcode))
+               {
+                       InitNew(); // leave crystal editor in valid, empty state
+                       return FileLoadResult::FRESULT_ERROR_UNPACK;
+               }
+       }
+       else
+       {
+               if (!FileTransform_Unpacking(sFileName, infoUnpacker, &unpackerSubcode))
+               {
+                       InitNew(); // leave crystal editor in valid, empty state
+                       return FileLoadResult::FRESULT_ERROR_UNPACK;
+               }
+       }
+       // we use the same unpacker for both files, so it must be defined after first file
+       ASSERT(infoUnpacker->bToBeScanned != PLUGIN_AUTO);
+       // we will load the transformed file
+       LPCTSTR pszFileName = sFileName.c_str();
+
+       String sExt;
+       DWORD nRetVal = FileLoadResult::FRESULT_OK;
+
+       // Set encoding based on extension, if we know one
+       SplitFilename(pszFileName, NULL, NULL, &sExt);
+       CCrystalTextView::TextDefinition *def = 
+               CCrystalTextView::GetTextType(sExt.c_str());
+       if (def && def->encoding != -1)
+               m_nSourceEncoding = def->encoding;
+       
+       UniFile *pufile = infoUnpacker->pufile;
+       if (pufile == 0)
+               pufile = new UniMemFile;
+
+       // Now we only use the UniFile interface
+       // which is something we could implement for HTTP and/or FTP files
+
+       if (!pufile->OpenReadOnly(pszFileName))
+       {
+               nRetVal = FileLoadResult::FRESULT_ERROR;
+               UniFile::UniError uniErr = pufile->GetLastUniError();
+               if (uniErr.hasError())
+               {
+                       if (uniErr.apiname.IsEmpty())
+                               sError = uniErr.desc;
+                       else
+                               sError = GetSysError(uniErr.syserrnum);
+               }
+               InitNew(); // leave crystal editor in valid, empty state
+               goto LoadFromFileExit;
+       }
+       else
+       {
+               // If the file is not unicode file, use the codepage we were given to
+               // interpret the 8-bit characters. If the file is unicode file,
+               // determine its type (IsUnicode() does that).
+               if (encoding.m_unicoding == ucr::NONE || !pufile->IsUnicode())
+                       pufile->SetCodepage(encoding.m_codepage);
+               UINT lineno = 0;
+               CString eol, preveol;
+               CString sline;
+               bool done = false;
+               UINT next_line_report = 100; // for trace messages
+               UINT next_line_multiple = 5; // for trace messages
+               COleDateTime start = COleDateTime::GetCurrentTime(); // for trace messages
+
+               // Manually grow line array exponentially
+               UINT arraysize = 500;
+               m_aLines.SetSize(arraysize);
+               
+               // preveol must be initialized for empty files
+               preveol = "\n";
+               
+               do {
+                       bool lossy=false;
+                       done = !pufile->ReadString(sline, eol, &lossy);
+
+                       // if last line had no eol, we can quit
+                       if (done && preveol.IsEmpty())
+                               break;
+                       // but if last line had eol, we add an extra (empty) line to buffer
+
+                       // Manually grow line array exponentially
+                       if (lineno == arraysize)
+                       {
+                               arraysize *= 2;
+                               m_aLines.SetSize(arraysize);
+                       }
+
+                       sline += eol; // TODO: opportunity for optimization, as CString append is terrible
+                       if (lossy)
+                       {
+                               // TODO: Should record lossy status of line
+                       }
+                       AppendLine(lineno, sline, sline.GetLength());
+                       ++lineno;
+                       preveol = eol;
+
+#ifdef _DEBUG
+                       // send occasional line counts to trace
+                       // (at 100, 500, 1000, 5000, etc)
+                       if (lineno == next_line_report)
+                       {
+                               __int64 dwBytesRead = pufile->GetPosition();
+                               COleDateTimeSpan duration = COleDateTime::GetCurrentTime() - start;
+                               if (duration.GetTotalMinutes() > 0)
+                               {
+                                       CString strace = GetLineByteTimeReport(lineno, dwBytesRead, start);
+                                       TRACE(_T("%s\n"), (LPCTSTR)strace);
+                               }
+                               next_line_report = next_line_multiple * next_line_report;
+                               next_line_multiple = (next_line_multiple == 5) ? 2 : 5;
+                       }
+#endif // _DEBUG
+               } while (!done);
+
+#ifdef _DEBUG
+               // Send report of duration to trace (if it took a while)
+               COleDateTime end = COleDateTime::GetCurrentTime();
+               COleDateTimeSpan duration = end - start;
+               if (duration.GetTotalMinutes() > 0)
+               {
+                       __int64 dwBytesRead = pufile->GetPosition();
+                       CString strace = GetLineByteTimeReport(lineno, dwBytesRead, start);
+                       TRACE(_T("%s\n"), (LPCTSTR)strace);
+               }
+#endif // _DEBUG
+
+               // fix array size (due to our manual exponential growth
+               m_aLines.SetSize(lineno);
+       
+               
+               //Try to determine current CRLF mode (most frequent)
+               if (nCrlfStyle == CRLF_STYLE_AUTOMATIC)
+               {
+                       nCrlfStyle = GetTextFileStyle(pufile->GetTxtStats());
+               }
+               ASSERT(nCrlfStyle >= 0 && nCrlfStyle <= 2);
+               SetCRLFMode(nCrlfStyle);
+               
+               //  At least one empty line must present
+               // (view does not work for empty buffers)
+               ASSERT(m_aLines.GetSize() > 0);
+               
+               m_bInit = TRUE;
+               m_bModified = FALSE;
+               m_bUndoGroup = m_bUndoBeginGroup = FALSE;
+               m_nUndoBufSize = 1024; // crystaltextbuffer.cpp - UNDO_BUF_SIZE;
+               m_nSyncPosition = m_nUndoPosition = 0;
+               ASSERT(m_aUndoBuf.GetSize() == 0);
+               m_ptLastChange.x = m_ptLastChange.y = -1;
+               
+               FinishLoading();
+               // flags don't need initialization because 0 is the default value
+
+               // Set the return value : OK + info if the file is impure
+               // A pure file is a file where EOL are consistent (all DOS, or all UNIX, or all MAC)
+               // An impure file is a file with several EOL types
+               // WinMerge may display impure files, but the default option is to unify the EOL
+               // We return this info to the caller, so it may display a confirmation box
+               if (IsTextFileStylePure(pufile->GetTxtStats()))
+                       nRetVal = FileLoadResult::FRESULT_OK;
+               else
+                       nRetVal = FileLoadResult::FRESULT_OK_IMPURE;
+
+               // stash original encoding away
+               m_encoding.m_unicoding = pufile->GetUnicoding();
+               m_encoding.m_bom = pufile->HasBom();
+               m_encoding.m_codepage = pufile->GetCodepage();
+
+               if (pufile->GetTxtStats().nlosses)
+               {
+                       FileLoadResult::AddModifier(nRetVal, FileLoadResult::FRESULT_LOSSY);
+                       readOnly = TRUE;
+               }
+       }
+       
+LoadFromFileExit:
+       // close the file now to free the handle
+       pufile->Close();
+       delete pufile;
+
+       // delete the file that unpacking may have created
+       if (_tcscmp(pszFileNameInit, pszFileName) != 0)
+               if (!::DeleteFile(pszFileName))
+               {
+                       LogErrorString(Fmt(_T("DeleteFile(%s) failed: %s"),
+                               pszFileName, GetSysError(GetLastError())));
+               }
+
+       return nRetVal;
+}
+
+/**
+ * @brief Saves file from buffer to disk
+ *
+ * @param bTempFile : FALSE if we are saving user files and
+ * TRUE if we are saving workin-temp-files for diff-engine
+ *
+ * @return SAVE_DONE or an error code (list in MergeDoc.h)
+ */
+int CDiffTextBuffer::SaveToFile (LPCTSTR pszFileName,
+               BOOL bTempFile, CString & sError, PackingInfo * infoUnpacker /*= NULL*/,
+               int nCrlfStyle /*= CRLF_STYLE_AUTOMATIC*/,
+               BOOL bClearModifiedFlag /*= TRUE*/ )
+{
+       ASSERT (nCrlfStyle == CRLF_STYLE_AUTOMATIC || nCrlfStyle == CRLF_STYLE_DOS ||
+               nCrlfStyle == CRLF_STYLE_UNIX || nCrlfStyle == CRLF_STYLE_MAC);
+       ASSERT (m_bInit);
+
+       if (!pszFileName || _tcslen(pszFileName) == 0)
+               return SAVE_FAILED;     // No filename, cannot save...
+
+       if (nCrlfStyle == CRLF_STYLE_AUTOMATIC &&
+               !GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
+               infoUnpacker && infoUnpacker->disallowMixedEOL)
+       {
+                       // get the default nCrlfStyle of the CDiffTextBuffer
+               nCrlfStyle = GetCRLFMode();
+               ASSERT(nCrlfStyle >= 0 && nCrlfStyle <= 2);
+       }
+
+       BOOL bOpenSuccess = TRUE;
+       BOOL bSaveSuccess = FALSE;
+
+       UniStdioFile file;
+       file.SetUnicoding(m_encoding.m_unicoding);
+       file.SetBom(m_encoding.m_bom);
+       file.SetCodepage(m_encoding.m_codepage);
+
+       String sIntermediateFilename; // used when !bTempFile
+
+       if (bTempFile)
+       {
+               bOpenSuccess = !!file.OpenCreate(pszFileName);
+       }
+       else
+       {
+               sIntermediateFilename = env_GetTempFileName(m_strTempPath.c_str(),
+                       _T("MRG_"), NULL);
+               if (sIntermediateFilename.empty())
+                       return SAVE_FAILED;  //Nothing to do if even tempfile name fails
+               bOpenSuccess = !!file.OpenCreate(sIntermediateFilename.c_str());
+       }
+
+       if (!bOpenSuccess)
+       {       
+               UniFile::UniError uniErr = file.GetLastUniError();
+               if (uniErr.hasError())
+               {
+                       if (uniErr.apiname.IsEmpty())
+                               sError = uniErr.desc;
+                       else
+                               sError = GetSysError(uniErr.syserrnum);
+                       if (bTempFile)
+                               LogErrorString(Fmt(_T("Opening file %s failed: %s"),
+                                       pszFileName, sError));
+                       else
+                               LogErrorString(Fmt(_T("Opening file %s failed: %s"),
+                                       sIntermediateFilename, sError));
+               }
+               return SAVE_FAILED;
+       }
+
+       file.WriteBom();
+
+       // line loop : get each real line and write it in the file
+       CString sLine;
+       CString sEol = GetStringEol(nCrlfStyle);
+       int nLineCount = m_aLines.GetSize();
+       for (int line=0; line<nLineCount; ++line)
+       {
+               if (GetLineFlags(line) & LF_GHOST)
+                       continue;
+
+               // get the characters of the line (excluding EOL)
+               if (GetLineLength(line) > 0)
+                       GetText(line, 0, line, GetLineLength(line), sLine, 0);
+               else
+                       sLine = _T("");
+
+               if (bTempFile)
+                       EscapeControlChars(sLine);
+               // last real line ?
+               if (line == ApparentLastRealLine())
+               {
+                       // last real line is never EOL terminated
+                       ASSERT (_tcslen(GetLineEol(line)) == 0);
+                       // write the line and exit loop
+                       file.WriteString(sLine);
+                       break;
+               }
+
+               // normal real line : append an EOL
+               if (nCrlfStyle == CRLF_STYLE_AUTOMATIC)
+               {
+                       // either the EOL of the line (when preserve original EOL chars is on)
+                       sLine += GetLineEol(line);
+               }
+               else
+               {
+                       // or the default EOL for this file
+                       sLine += sEol;
+               }
+
+               // write this line to the file (codeset or unicode conversions are done there)
+               file.WriteString(sLine);
+       }
+       file.Close();
+
+
+       if (!bTempFile)
+       {
+               // If we are saving user files
+               // we need an unpacker/packer, at least a "do nothing" one
+               ASSERT(infoUnpacker != NULL);
+               // repack the file here, overwrite the temporary file we did save in
+               String csTempFileName = sIntermediateFilename;
+               infoUnpacker->subcode = unpackerSubcode;
+               if (!FileTransform_Packing(csTempFileName, *infoUnpacker))
+               {
+                       if (!::DeleteFile(sIntermediateFilename.c_str()))
+                       {
+                               LogErrorString(Fmt(_T("DeleteFile(%s) failed: %s"),
+                                       sIntermediateFilename.c_str(), GetSysError(GetLastError())));
+                       }
+                       // returns now, don't overwrite the original file
+                       return SAVE_PACK_FAILED;
+               }
+               // the temp filename may have changed during packing
+               if (csTempFileName != sIntermediateFilename)
+               {
+                       if (!::DeleteFile(sIntermediateFilename.c_str()))
+                       {
+                               LogErrorString(Fmt(_T("DeleteFile(%s) failed: %s"),
+                                       sIntermediateFilename.c_str(), GetSysError(GetLastError())));
+                       }
+                       sIntermediateFilename = csTempFileName;
+               }
+
+               // Write tempfile over original file
+               if (::CopyFile(sIntermediateFilename.c_str(), pszFileName, FALSE))
+               {
+                       if (!::DeleteFile(sIntermediateFilename.c_str()))
+                       {
+                               LogErrorString(Fmt(_T("DeleteFile(%s) failed: %s"),
+                                       sIntermediateFilename.c_str(), GetSysError(GetLastError())));
+                       }
+                       if (bClearModifiedFlag)
+                       {
+                               SetModified(FALSE);
+                               m_nSyncPosition = m_nUndoPosition;
+                       }
+                       bSaveSuccess = TRUE;
+
+                       // remember revision number on save
+                       m_dwRevisionNumberOnSave = m_dwCurrentRevisionNumber;
+
+                       // redraw line revision marks
+                       UpdateViews (NULL, NULL, UPDATE_FLAGSONLY);     
+               }
+               else
+               {
+                       sError = GetSysError(GetLastError());
+                       LogErrorString(Fmt(_T("CopyFile(%s, %s) failed: %s"),
+                               sIntermediateFilename.c_str(), pszFileName, sError));
+               }
+       }
+       else
+       {
+               if (bClearModifiedFlag)
+               {
+                       SetModified(FALSE);
+                       m_nSyncPosition = m_nUndoPosition;
+               }
+               bSaveSuccess = TRUE;
+       }
+
+       if (bSaveSuccess)
+               return SAVE_DONE;
+       else
+               return SAVE_FAILED;
+}
+
+/// Replace text of line (no change to eol)
+void CDiffTextBuffer::ReplaceLine(CCrystalTextView * pSource, int nLine, LPCTSTR pchText, int cchText, int nAction /*=CE_ACTION_UNKNOWN*/)
+{
+       if (GetLineLength(nLine)>0)
+               DeleteText(pSource, nLine, 0, nLine, GetLineLength(nLine), nAction);
+       int endl, endc;
+       if (cchText)
+               InsertText(pSource, nLine, 0, pchText, cchText, endl, endc, nAction);
+}
+
+/// Replace line (removing any eol, and only including one if in strText)
+void CDiffTextBuffer::ReplaceFullLine(CCrystalTextView * pSource, int nLine, const CString &strText, int nAction /*=CE_ACTION_UNKNOWN*/)
+{
+       if (_tcscmp(GetLineEol(nLine), getEol(strText)) == 0)
+       {
+               // (optimization) eols are the same, so just replace text inside line
+               // we must clean strText from its eol...
+               int eolLength = _tcslen(getEol(strText));
+               ReplaceLine(pSource, nLine, strText, strText.GetLength() - eolLength, nAction);
+               return;
+       }
+
+       // we may need a last line as the DeleteText end is (x=0,y=line+1)
+       if (nLine+1 == GetLineCount())
+               InsertGhostLine (pSource, GetLineCount());
+
+       if (GetFullLineLength(nLine))
+               DeleteText(pSource, nLine, 0, nLine+1, 0, nAction); 
+       int endl,endc;
+       if (int cchText = strText.GetLength())
+               InsertText(pSource, nLine, 0, strText, cchText, endl,endc, nAction);
+}
+
+bool CDiffTextBuffer::curUndoGroup()
+{
+       return (m_aUndoBuf.GetSize()!=0 && m_aUndoBuf[0].m_dwFlags&UNDO_BEGINGROUP);
+}
diff --git a/Src/DiffTextBuffer.h b/Src/DiffTextBuffer.h
new file mode 100644 (file)
index 0000000..65df3c8
--- /dev/null
@@ -0,0 +1,87 @@
+/** 
+ * @file  DiffTextBuffer.h
+ *
+ * @brief Declaration of CDiffTextBuffer class
+ */
+// ID line follows -- this is updated by SVN
+// $Id$
+
+#ifndef _DIFFTEXT_BUFFER_H_
+#define _DIFFTEXT_BUFFER_H_
+
+#include "GhostTextBuffer.h"
+#include "FileTextEncoding.h"
+
+class CMergedoc;
+class PackingInfo;
+
+/**
+ * @brief Specialized buffer to save file data
+ */
+class CDiffTextBuffer : public CGhostTextBuffer
+{
+       friend class CMergeDoc;
+
+private :
+       CMergeDoc * m_pOwnerDoc; /**< Merge document owning this buffer. */
+       int m_nThisPane; /**< Left/Right side */
+       BOOL FlagIsSet(UINT line, DWORD flag);
+       String m_strTempPath; /**< Temporary files folder. */
+       int unpackerSubcode;
+       /* 
+               * @brief Unicode encoding from ucr::UNICODESET 
+               *
+               * @note m_unicoding and m_codepage are indications of how the buffer is supposed to be saved on disk
+               * In memory, it is invariant, depending on build:
+               * ANSI:
+               *   in memory it is CP_ACP/CP_THREAD_ACP 8-bit characters
+               * Unicode:
+               *   in memory it is wchars
+               */
+       FileTextEncoding m_encoding; /**< File's encoding information. */
+
+       int NoteCRLFStyleFromBuffer(TCHAR *lpLineBegin, DWORD dwLineLen = 0);
+       void ReadLineFromBuffer(TCHAR *lpLineBegin, DWORD dwLineNum, DWORD dwLineLen = 0);
+public :
+       void SetTempPath(String path);
+       virtual void AddUndoRecord (BOOL bInsert, const CPoint & ptStartPos, const CPoint & ptEndPos,
+               LPCTSTR pszText, int cchText, int nLinesToValidate, int nActionType = CE_ACTION_UNKNOWN, CDWordArray *paSavedRevisonNumbers = NULL);
+       bool curUndoGroup();
+       void ReplaceLine(CCrystalTextView * pSource, int nLine, LPCTSTR pchText, int cchText, int nAction =CE_ACTION_UNKNOWN);
+       void ReplaceFullLine(CCrystalTextView * pSource, int nLine, const CString& strText, int nAction =CE_ACTION_UNKNOWN);
+
+       int LoadFromFile(LPCTSTR pszFileName, PackingInfo * infoUnpacker,
+               LPCTSTR filteredFilenames, BOOL & readOnly, int nCrlfStyle,
+               const FileTextEncoding & encoding, CString &sError);
+       int SaveToFile (LPCTSTR pszFileName, BOOL bTempFile, CString & sError,
+               PackingInfo * infoUnpacker = NULL, int nCrlfStyle = CRLF_STYLE_AUTOMATIC,
+               BOOL bClearModifiedFlag = TRUE );
+       int getUnicoding() const { return m_encoding.m_unicoding; }
+       void setUnicoding(int value) { m_encoding.m_unicoding = value; }
+       int getCodepage() const { return m_encoding.m_codepage; }
+       void setCodepage(int value) { m_encoding.m_codepage = value; }
+       const FileTextEncoding & getEncoding() const { return m_encoding; }
+
+       CDiffTextBuffer(CMergeDoc * pDoc, int pane);
+
+       // If line has text (excluding eol), set strLine to text (excluding eol)
+       BOOL GetLine(int nLineIndex, CString &strLine);
+
+       // if line has any text (including eol), set strLine to text (including eol)
+       BOOL GetFullLine(int nLineIndex, CString &strLine);
+
+       virtual void SetModified (BOOL bModified = TRUE);
+
+       void prepareForRescan();
+
+       /** 
+       After editing a line, we don't know if there is a diff or not.
+       So we clear the LF_DIFF flag (and it is more easy to read during edition).
+       Rescan will set the proper color
+       */
+       virtual void OnNotifyLineHasBeenEdited(int nLine);
+
+       bool IsInitialized() const;
+};
+
+#endif // _DIFFTEXT_BUFFER_H_
index 8ef9044..1710ec9 100644 (file)
                                </FileConfiguration>
                        </File>
                        <File
+                               RelativePath="DiffTextBuffer.cpp">
+                               <FileConfiguration
+                                       Name="UnicodeRelease|Win32">
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                               Optimization="1"
+                                               AdditionalIncludeDirectories=""
+                                               PreprocessorDefinitions=""/>
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="UnicodeDebug|Win32">
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                               Optimization="0"
+                                               AdditionalIncludeDirectories=""
+                                               PreprocessorDefinitions=""
+                                               BrowseInformation="1"/>
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="Debug|Win32">
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                               Optimization="0"
+                                               AdditionalIncludeDirectories=""
+                                               PreprocessorDefinitions=""
+                                               BrowseInformation="1"/>
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="Release|Win32">
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                               Optimization="1"
+                                               AdditionalIncludeDirectories=""
+                                               PreprocessorDefinitions=""/>
+                               </FileConfiguration>
+                       </File>
+                       <File
                                RelativePath="DiffThread.cpp">
                                <FileConfiguration
                                        Name="UnicodeRelease|Win32">
                                RelativePath="DiffList.h">
                        </File>
                        <File
+                               RelativePath="DiffTextBuffer.h">
+                       </File>
+                       <File
                                RelativePath="DiffThread.h">
                        </File>
                        <File
index 4272f6e..fad42ae 100644 (file)
@@ -30,9 +30,9 @@
 #include "stdafx.h"
 #include <Shlwapi.h>           // PathCompactPathEx()
 #include "UnicodeString.h"
-#include "FileTextEncoding.h"
 #include "Merge.h"
 #include "MainFrm.h"
+#include "DiffTextBuffer.h"
 #include "Environment.h"
 #include "Ucs2Utf8.h"
 #include "diffcontext.h"       // FILE_SAME
@@ -42,7 +42,6 @@
 #include "coretools.h"
 #include "MergeEditView.h"
 #include "MergeDiffDetailView.h"
-#include "cs2cs.h"
 #include "childFrm.h"
 #include "dirdoc.h"
 #include "files.h"
@@ -50,7 +49,6 @@
 #include "FileTransform.h"
 #include "unicoder.h"
 #include "UniFile.h"
-#include "locality.h"
 #include "OptionsDef.h"
 #include "DiffFileInfo.h"
 #include "SaveClosingDlg.h"
@@ -82,9 +80,6 @@ static LPCTSTR crlfs[] =
        _T ("\x0d")      //  Macintosh style
 };
 
-static CString GetLineByteTimeReport(UINT lines, __int64 bytes,
-       const COleDateTime & start);
-
 /////////////////////////////////////////////////////////////////////////////
 // CMergeDoc
 
@@ -334,7 +329,7 @@ void CMergeDoc::Serialize(CArchive& ar)
  * (the plugins are optional, not the conversion)
  * @todo Show SaveToFile() errors?
  */
-static void SaveBuffForDiff(CMergeDoc::CDiffTextBuffer & buf, LPCTSTR filepath)
+static void SaveBuffForDiff(CDiffTextBuffer & buf, LPCTSTR filepath)
 {
        ASSERT(buf.m_nSourceEncoding == buf.m_nDefaultEncoding);  
        int orig_codepage = buf.getCodepage();
@@ -657,59 +652,6 @@ void CMergeDoc::ShowRescanError(int nRescanResult, BOOL bIdentical)
        }
 }
 
-CMergeDoc::CDiffTextBuffer::CDiffTextBuffer(CMergeDoc * pDoc, int pane)
-: m_pOwnerDoc(pDoc)
-, m_nThisPane(pane)
-, unpackerSubcode(0)
-{
-}
-
-BOOL CMergeDoc::CDiffTextBuffer::GetLine(int nLineIndex, CString &strLine)
-{ 
-       int nLineLength = CCrystalTextBuffer::GetLineLength 
-               ( nLineIndex ); 
-       
-       if( nLineLength < 0 ) 
-               return FALSE; 
-       else if( nLineLength == 0 ) 
-               strLine.Empty(); 
-       else 
-       { 
-               _tcsncpy ( strLine.GetBuffer( nLineLength + 1 ), 
-                       CCrystalTextBuffer::GetLineChars( nLineIndex ), 
-                       nLineLength ); 
-               strLine.ReleaseBuffer( nLineLength ); 
-       } 
-       return TRUE; 
-}
-
-void CMergeDoc::CDiffTextBuffer::SetModified(BOOL bModified /*= TRUE*/)
-{
-       CCrystalTextBuffer::SetModified (bModified);
-       m_pOwnerDoc->SetModifiedFlag (bModified);
-}
-
-BOOL CMergeDoc::CDiffTextBuffer::GetFullLine(int nLineIndex, CString &strLine)
-{
-       int cchText = GetFullLineLength(nLineIndex);
-       if (cchText == 0)
-               return FALSE;
-       LPTSTR pchText = strLine.GetBufferSetLength(cchText);
-       memcpy(pchText, GetLineChars(nLineIndex), cchText * sizeof(TCHAR));
-       return TRUE;
-}
-
-void CMergeDoc::CDiffTextBuffer::AddUndoRecord(BOOL bInsert, const CPoint & ptStartPos, const CPoint & ptEndPos, LPCTSTR pszText, int cchText, int nLinesToValidate, int nActionType /*= CE_ACTION_UNKNOWN*/, CDWordArray *paSavedRevisonNumbers)
-{
-       CGhostTextBuffer::AddUndoRecord(bInsert, ptStartPos, ptEndPos, pszText, cchText, nLinesToValidate, nActionType, paSavedRevisonNumbers);
-       if (m_aUndoBuf[m_nUndoPosition - 1].m_dwFlags & UNDO_BEGINGROUP)
-       {
-               m_pOwnerDoc->undoTgt.erase(m_pOwnerDoc->curUndo, m_pOwnerDoc->undoTgt.end());
-               m_pOwnerDoc->undoTgt.push_back(m_pOwnerDoc->m_pView[m_nThisPane]);
-               m_pOwnerDoc->curUndo = m_pOwnerDoc->undoTgt.end();
-       }
-}
-
 BOOL CMergeDoc::Undo()
 {
        return FALSE;
@@ -1360,218 +1302,6 @@ void CMergeDoc::SetCurrentDiff(int nDiff)
 }
 
 /**
- * @brief Checks if a flag is set for line.
- * @param [in] line Index (0-based) for line.
- * @param [in] flag Flag to check.
- * @return TRUE if flag is set, FALSE otherwise.
- */
-BOOL CMergeDoc::CDiffTextBuffer::FlagIsSet(UINT line, DWORD flag)
-{
-       return ((m_aLines[line].m_dwFlags & flag) == flag);
-}
-
-
-/**
-Remove blank lines and clear winmerge flags
-(2003-06-21, Perry: I don't understand why this is necessary, but if this isn't 
-done, more and more gray lines appear in the file)
-(2003-07-31, Laoran I don't understand either why it is necessary, but it works
-fine, so let's go on with it)
-*/
-void CMergeDoc::CDiffTextBuffer::prepareForRescan()
-{
-       RemoveAllGhostLines();
-       for(int ct=GetLineCount()-1; ct>=0; --ct)
-       {
-               SetLineFlag(ct, LF_DIFF, FALSE, FALSE, FALSE);
-               SetLineFlag(ct, LF_TRIVIAL, FALSE, FALSE, FALSE);
-               SetLineFlag(ct, LF_MOVED, FALSE, FALSE, FALSE);
-       }
-}
-
-
-void CMergeDoc::CDiffTextBuffer::OnNotifyLineHasBeenEdited(int nLine)
-{
-       SetLineFlag(nLine, LF_DIFF, FALSE, FALSE, FALSE);
-       SetLineFlag(nLine, LF_TRIVIAL, FALSE, FALSE, FALSE);
-       SetLineFlag(nLine, LF_MOVED, FALSE, FALSE, FALSE);
-       CGhostTextBuffer::OnNotifyLineHasBeenEdited(nLine);
-}
-
-
-
-/**
- * @brief Helper to determine the most used CRLF mode in the file
- * Normal call : determine the CRLF mode of the current line
- * Final call  : parameter lpLineBegin = NULL, return the style of the file
- *
- * @note  The lowest CRL_STYLE has the priority in case of equality
- */
-int CMergeDoc::CDiffTextBuffer::NoteCRLFStyleFromBuffer(TCHAR *lpLineBegin, DWORD dwLineLen /* =0 */)
-{
-       static int count[3] = {0};
-       int iStyle = -1;
-
-       // give back the result when we ask for it
-       if (lpLineBegin == NULL)
-       {
-               iStyle = 0;
-               if (count[1] > count[iStyle])
-                       iStyle = 1;
-               if (count[2] > count[iStyle])
-                       iStyle = 2;
-
-               // reset the counter for the next file
-               count[0] = count[1] = count[2] = 0;
-
-               return iStyle;
-       }
-
-       if (dwLineLen >= 1)
-       {
-               if (lpLineBegin[dwLineLen-1] == _T('\r'))
-                       iStyle = CRLF_STYLE_MAC;
-               if (lpLineBegin[dwLineLen-1] == _T('\n'))
-                       iStyle = CRLF_STYLE_UNIX;
-       }
-       if (dwLineLen >= 2)
-       {
-               if (lpLineBegin[dwLineLen-2] == _T('\r') && lpLineBegin[dwLineLen-1] == _T('\n'))
-                       iStyle = CRLF_STYLE_DOS;
-       }
-       ASSERT (iStyle != -1);
-       count[iStyle] ++;
-       return iStyle;
-}
-
-/// Reads one line from filebuffer and inserts to textbuffer
-void CMergeDoc::CDiffTextBuffer::ReadLineFromBuffer(TCHAR *lpLineBegin, DWORD dwLineNum, DWORD dwLineLen /* =0 */)
-{
-       if (m_nSourceEncoding >= 0)
-               iconvert (lpLineBegin, m_nSourceEncoding, 1, m_nSourceEncoding == 15);
-       AppendLine(dwLineNum, lpLineBegin, dwLineLen);
-}
-
-/// Sets path for temporary files
-void CMergeDoc::CDiffTextBuffer::SetTempPath(String path)
-{
-       m_strTempPath = path;
-}
-
-bool CMergeDoc::CDiffTextBuffer::IsInitialized() const
-{
-       return !!m_bInit;
-}
-
-/**
- * @brief Examine statistics in textFileStats and return a crystaltextbuffer enum value for line style
- */
-int GetTextFileStyle(const UniMemFile::txtstats & stats)
-{
-       if (stats.ncrlfs >= stats.nlfs)
-       {
-               if (stats.ncrlfs >= stats.ncrs)
-               {
-                       return CRLF_STYLE_DOS;
-               }
-               else
-               {
-                       return CRLF_STYLE_MAC;
-               }
-       }
-       else
-       {
-               if (stats.nlfs >= stats.ncrs)
-               {
-                       return CRLF_STYLE_UNIX;
-               }
-               else
-               {
-                       return CRLF_STYLE_MAC;
-               }
-       }
-}
-/**
- * @brief Examine statistics in textFileStats and tell if the file has only one EOL type
- */
-int IsTextFileStylePure(const UniMemFile::txtstats & stats)
-{
-       int nType = 0;
-       nType += (stats.ncrlfs > 0);
-       nType += (stats.ncrs > 0);
-       nType += (stats.nlfs > 0);
-       return (nType <= 1);
-}
-
-
-/**
- * @brief Return a string giving #lines and #bytes and how much time elapsed.
- * @param [in] lines Count of lines.
- * @param [in] bytes Count of bytes.
- * @param [in] start Time used.
- * @return Formatted string.
- */
-static CString GetLineByteTimeReport(UINT lines, __int64 bytes,
-       const COleDateTime & start)
-{
-       String sLines = locality::NumToLocaleStr((int)lines);
-       String sBytes = locality::NumToLocaleStr(bytes);
-       COleDateTimeSpan duration = COleDateTime::GetCurrentTime() - start;
-       String sMinutes = locality::NumToLocaleStr((int)duration.GetTotalMinutes());
-       CString str;
-       str.Format(_T("%s lines (%s byte) saved in %sm%02ds")
-               , sLines.c_str(), sBytes.c_str(), sMinutes.c_str()
-               , duration.GetSeconds()
-               );
-       return str;
-}
-
-/**
- * @brief Escape control characters.
- * @param [in,out] s Line of text excluding eol chars.
- *
- * @note Escape sequences follow the pattern
- * (leadin character, high nibble, low nibble, leadout character).
- * The leadin character is '\x0F'. The leadout character is a backslash.
- */
-static void EscapeControlChars(CString &s)
-{
-       // Compute buffer length required for escaping
-       int n = s.GetLength();
-       LPCTSTR q = s;
-       int i = n;
-       while (i)
-       {
-               TCHAR c = q[--i];
-               // Is it a control character in the range 0..31 except TAB?
-               if (!(c & ~_T('\x1F')) && c != _T('\t'))
-               {
-                       n += 3; // Need 3 extra characters to escape
-               }
-       }
-       // Reallocate accordingly
-       i = s.GetLength();
-       LPTSTR p = s.GetBufferSetLength(n);
-       // Copy/translate characters starting at end of string
-       while (i)
-       {
-               TCHAR c = p[--i];
-               // Is it a control character in the range 0..31 except TAB?
-               if (!(c & ~_T('\x1F')) && c != _T('\t'))
-               {
-                       // Bitwise OR with 0x100 so _itot() will output 3 hex digits
-                       _itot(0x100 | c, p + n - 4, 16);
-                       // Replace terminating zero with leadout character
-                       p[n - 1] = _T('\\');
-                       // Prepare to replace 1st hex digit with leadin character
-                       c = _T('\x0F');
-                       n -= 3;
-               }
-               p[--n] = c;
-       }
-}
-
-/**
  * @brief Unescape control characters.
  * @param [in,out] s Line of text excluding eol chars.
  */
@@ -1602,460 +1332,6 @@ static void UnescapeControlChars(CString &s)
 }
 
 /**
- * @brief Load file from disk into buffer
- *
- * @param [in] pszFileNameInit File to load
- * @param [in] infoUnpacker Unpacker plugin
- * @param [in] sToFindUnpacker String for finding unpacker plugin
- * @param [out] readOnly Loading was lossy so file should be read-only
- * @param [in] nCrlfStyle EOL style used
- * @param [in] encoding Encoding used
- * @param [out] sError Error message returned
- * @return FRESULT_OK when loading succeed or (list in files.h):
- * - FRESULT_OK_IMPURE : load OK, but the EOL are of different types
- * - FRESULT_ERROR_UNPACK : plugin failed to unpack
- * - FRESULT_ERROR : loading failed, sError contains error message
- * - FRESULT_BINARY : file is binary file
- * @note If this method fails, it calls InitNew so the CDiffTextBuffer is in a valid state
- */
-int CMergeDoc::CDiffTextBuffer::LoadFromFile(LPCTSTR pszFileNameInit,
-               PackingInfo * infoUnpacker, LPCTSTR sToFindUnpacker, BOOL & readOnly,
-               int nCrlfStyle, const FileTextEncoding & encoding, CString &sError)
-{
-       ASSERT(!m_bInit);
-       ASSERT(m_aLines.GetSize() == 0);
-
-       // Unpacking the file here, save the result in a temporary file
-       String sFileName = pszFileNameInit;
-       if (infoUnpacker->bToBeScanned)
-       {
-               if (!FileTransform_Unpacking(sFileName, sToFindUnpacker, infoUnpacker, &unpackerSubcode))
-               {
-                       InitNew(); // leave crystal editor in valid, empty state
-                       return FileLoadResult::FRESULT_ERROR_UNPACK;
-               }
-       }
-       else
-       {
-               if (!FileTransform_Unpacking(sFileName, infoUnpacker, &unpackerSubcode))
-               {
-                       InitNew(); // leave crystal editor in valid, empty state
-                       return FileLoadResult::FRESULT_ERROR_UNPACK;
-               }
-       }
-       // we use the same unpacker for both files, so it must be defined after first file
-       ASSERT(infoUnpacker->bToBeScanned != PLUGIN_AUTO);
-       // we will load the transformed file
-       LPCTSTR pszFileName = sFileName.c_str();
-
-       String sExt;
-       DWORD nRetVal = FileLoadResult::FRESULT_OK;
-
-       // Set encoding based on extension, if we know one
-       SplitFilename(pszFileName, NULL, NULL, &sExt);
-       CCrystalTextView::TextDefinition *def = 
-               CCrystalTextView::GetTextType(sExt.c_str());
-       if (def && def->encoding != -1)
-               m_nSourceEncoding = def->encoding;
-       
-       UniFile *pufile = infoUnpacker->pufile;
-       if (pufile == 0)
-               pufile = new UniMemFile;
-
-       // Now we only use the UniFile interface
-       // which is something we could implement for HTTP and/or FTP files
-
-       if (!pufile->OpenReadOnly(pszFileName))
-       {
-               nRetVal = FileLoadResult::FRESULT_ERROR;
-               UniFile::UniError uniErr = pufile->GetLastUniError();
-               if (uniErr.hasError())
-               {
-                       if (uniErr.apiname.IsEmpty())
-                               sError = uniErr.desc;
-                       else
-                               sError = GetSysError(uniErr.syserrnum);
-               }
-               InitNew(); // leave crystal editor in valid, empty state
-               goto LoadFromFileExit;
-       }
-       else
-       {
-               // If the file is not unicode file, use the codepage we were given to
-               // interpret the 8-bit characters. If the file is unicode file,
-               // determine its type (IsUnicode() does that).
-               if (encoding.m_unicoding == ucr::NONE || !pufile->IsUnicode())
-                       pufile->SetCodepage(encoding.m_codepage);
-               UINT lineno = 0;
-               CString eol, preveol;
-               CString sline;
-               bool done = false;
-               UINT next_line_report = 100; // for trace messages
-               UINT next_line_multiple = 5; // for trace messages
-               COleDateTime start = COleDateTime::GetCurrentTime(); // for trace messages
-
-               // Manually grow line array exponentially
-               UINT arraysize = 500;
-               m_aLines.SetSize(arraysize);
-               
-               // preveol must be initialized for empty files
-               preveol = "\n";
-               
-               do {
-                       bool lossy=false;
-                       done = !pufile->ReadString(sline, eol, &lossy);
-
-                       // if last line had no eol, we can quit
-                       if (done && preveol.IsEmpty())
-                               break;
-                       // but if last line had eol, we add an extra (empty) line to buffer
-
-                       // Manually grow line array exponentially
-                       if (lineno == arraysize)
-                       {
-                               arraysize *= 2;
-                               m_aLines.SetSize(arraysize);
-                       }
-
-                       sline += eol; // TODO: opportunity for optimization, as CString append is terrible
-                       if (lossy)
-                       {
-                               // TODO: Should record lossy status of line
-                       }
-                       AppendLine(lineno, sline, sline.GetLength());
-                       ++lineno;
-                       preveol = eol;
-
-#ifdef _DEBUG
-                       // send occasional line counts to trace
-                       // (at 100, 500, 1000, 5000, etc)
-                       if (lineno == next_line_report)
-                       {
-                               __int64 dwBytesRead = pufile->GetPosition();
-                               COleDateTimeSpan duration = COleDateTime::GetCurrentTime() - start;
-                               if (duration.GetTotalMinutes() > 0)
-                               {
-                                       CString strace = GetLineByteTimeReport(lineno, dwBytesRead, start);
-                                       TRACE(_T("%s\n"), (LPCTSTR)strace);
-                               }
-                               next_line_report = next_line_multiple * next_line_report;
-                               next_line_multiple = (next_line_multiple == 5) ? 2 : 5;
-                       }
-#endif // _DEBUG
-               } while (!done);
-
-#ifdef _DEBUG
-               // Send report of duration to trace (if it took a while)
-               COleDateTime end = COleDateTime::GetCurrentTime();
-               COleDateTimeSpan duration = end - start;
-               if (duration.GetTotalMinutes() > 0)
-               {
-                       __int64 dwBytesRead = pufile->GetPosition();
-                       CString strace = GetLineByteTimeReport(lineno, dwBytesRead, start);
-                       TRACE(_T("%s\n"), (LPCTSTR)strace);
-               }
-#endif // _DEBUG
-
-               // fix array size (due to our manual exponential growth
-               m_aLines.SetSize(lineno);
-       
-               
-               //Try to determine current CRLF mode (most frequent)
-               if (nCrlfStyle == CRLF_STYLE_AUTOMATIC)
-               {
-                       nCrlfStyle = GetTextFileStyle(pufile->GetTxtStats());
-               }
-               ASSERT(nCrlfStyle >= 0 && nCrlfStyle <= 2);
-               SetCRLFMode(nCrlfStyle);
-               
-               //  At least one empty line must present
-               // (view does not work for empty buffers)
-               ASSERT(m_aLines.GetSize() > 0);
-               
-               m_bInit = TRUE;
-               m_bModified = FALSE;
-               m_bUndoGroup = m_bUndoBeginGroup = FALSE;
-               m_nUndoBufSize = 1024; // crystaltextbuffer.cpp - UNDO_BUF_SIZE;
-               m_nSyncPosition = m_nUndoPosition = 0;
-               ASSERT(m_aUndoBuf.GetSize() == 0);
-               m_ptLastChange.x = m_ptLastChange.y = -1;
-               
-               FinishLoading();
-               // flags don't need initialization because 0 is the default value
-
-               // Set the return value : OK + info if the file is impure
-               // A pure file is a file where EOL are consistent (all DOS, or all UNIX, or all MAC)
-               // An impure file is a file with several EOL types
-               // WinMerge may display impure files, but the default option is to unify the EOL
-               // We return this info to the caller, so it may display a confirmation box
-               if (IsTextFileStylePure(pufile->GetTxtStats()))
-                       nRetVal = FileLoadResult::FRESULT_OK;
-               else
-                       nRetVal = FileLoadResult::FRESULT_OK_IMPURE;
-
-               // stash original encoding away
-               m_encoding.m_unicoding = pufile->GetUnicoding();
-               m_encoding.m_bom = pufile->HasBom();
-               m_encoding.m_codepage = pufile->GetCodepage();
-
-               if (pufile->GetTxtStats().nlosses)
-               {
-                       FileLoadResult::AddModifier(nRetVal, FileLoadResult::FRESULT_LOSSY);
-                       readOnly = TRUE;
-               }
-       }
-       
-LoadFromFileExit:
-       // close the file now to free the handle
-       pufile->Close();
-       delete pufile;
-
-       // delete the file that unpacking may have created
-       if (_tcscmp(pszFileNameInit, pszFileName) != 0)
-               if (!::DeleteFile(pszFileName))
-               {
-                       LogErrorString(Fmt(_T("DeleteFile(%s) failed: %s"),
-                               pszFileName, GetSysError(GetLastError())));
-               }
-
-       return nRetVal;
-}
-
-/**
- * @brief Saves file from buffer to disk
- *
- * @param bTempFile : FALSE if we are saving user files and
- * TRUE if we are saving workin-temp-files for diff-engine
- *
- * @return SAVE_DONE or an error code (list in MergeDoc.h)
- */
-int CMergeDoc::CDiffTextBuffer::SaveToFile (LPCTSTR pszFileName,
-               BOOL bTempFile, CString & sError, PackingInfo * infoUnpacker /*= NULL*/,
-               int nCrlfStyle /*= CRLF_STYLE_AUTOMATIC*/,
-               BOOL bClearModifiedFlag /*= TRUE*/ )
-{
-       ASSERT (nCrlfStyle == CRLF_STYLE_AUTOMATIC || nCrlfStyle == CRLF_STYLE_DOS ||
-               nCrlfStyle == CRLF_STYLE_UNIX || nCrlfStyle == CRLF_STYLE_MAC);
-       ASSERT (m_bInit);
-
-       if (!pszFileName || _tcslen(pszFileName) == 0)
-               return SAVE_FAILED;     // No filename, cannot save...
-
-       if (nCrlfStyle == CRLF_STYLE_AUTOMATIC &&
-               !GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
-               infoUnpacker && infoUnpacker->disallowMixedEOL)
-       {
-                       // get the default nCrlfStyle of the CDiffTextBuffer
-               nCrlfStyle = GetCRLFMode();
-               ASSERT(nCrlfStyle >= 0 && nCrlfStyle <= 2);
-       }
-
-       BOOL bOpenSuccess = TRUE;
-       BOOL bSaveSuccess = FALSE;
-
-       UniStdioFile file;
-       file.SetUnicoding(m_encoding.m_unicoding);
-       file.SetBom(m_encoding.m_bom);
-       file.SetCodepage(m_encoding.m_codepage);
-
-       String sIntermediateFilename; // used when !bTempFile
-
-       if (bTempFile)
-       {
-               bOpenSuccess = !!file.OpenCreate(pszFileName);
-       }
-       else
-       {
-               sIntermediateFilename = env_GetTempFileName(m_strTempPath.c_str(),
-                       _T("MRG_"), NULL);
-               if (sIntermediateFilename.empty())
-                       return SAVE_FAILED;  //Nothing to do if even tempfile name fails
-               bOpenSuccess = !!file.OpenCreate(sIntermediateFilename.c_str());
-       }
-
-       if (!bOpenSuccess)
-       {       
-               UniFile::UniError uniErr = file.GetLastUniError();
-               if (uniErr.hasError())
-               {
-                       if (uniErr.apiname.IsEmpty())
-                               sError = uniErr.desc;
-                       else
-                               sError = GetSysError(uniErr.syserrnum);
-                       if (bTempFile)
-                               LogErrorString(Fmt(_T("Opening file %s failed: %s"),
-                                       pszFileName, sError));
-                       else
-                               LogErrorString(Fmt(_T("Opening file %s failed: %s"),
-                                       sIntermediateFilename, sError));
-               }
-               return SAVE_FAILED;
-       }
-
-       file.WriteBom();
-
-       // line loop : get each real line and write it in the file
-       CString sLine;
-       CString sEol = GetStringEol(nCrlfStyle);
-       int nLineCount = m_aLines.GetSize();
-       for (int line=0; line<nLineCount; ++line)
-       {
-               if (GetLineFlags(line) & LF_GHOST)
-                       continue;
-
-               // get the characters of the line (excluding EOL)
-               if (GetLineLength(line) > 0)
-                       GetText(line, 0, line, GetLineLength(line), sLine, 0);
-               else
-                       sLine = _T("");
-
-               if (bTempFile)
-                       EscapeControlChars(sLine);
-               // last real line ?
-               if (line == ApparentLastRealLine())
-               {
-                       // last real line is never EOL terminated
-                       ASSERT (_tcslen(GetLineEol(line)) == 0);
-                       // write the line and exit loop
-                       file.WriteString(sLine);
-                       break;
-               }
-
-               // normal real line : append an EOL
-               if (nCrlfStyle == CRLF_STYLE_AUTOMATIC)
-               {
-                       // either the EOL of the line (when preserve original EOL chars is on)
-                       sLine += GetLineEol(line);
-               }
-               else
-               {
-                       // or the default EOL for this file
-                       sLine += sEol;
-               }
-
-               // write this line to the file (codeset or unicode conversions are done there)
-               file.WriteString(sLine);
-       }
-       file.Close();
-
-
-       if (!bTempFile)
-       {
-               // If we are saving user files
-               // we need an unpacker/packer, at least a "do nothing" one
-               ASSERT(infoUnpacker != NULL);
-               // repack the file here, overwrite the temporary file we did save in
-               String csTempFileName = sIntermediateFilename;
-               infoUnpacker->subcode = unpackerSubcode;
-               if (!FileTransform_Packing(csTempFileName, *infoUnpacker))
-               {
-                       if (!::DeleteFile(sIntermediateFilename.c_str()))
-                       {
-                               LogErrorString(Fmt(_T("DeleteFile(%s) failed: %s"),
-                                       sIntermediateFilename.c_str(), GetSysError(GetLastError())));
-                       }
-                       // returns now, don't overwrite the original file
-                       return SAVE_PACK_FAILED;
-               }
-               // the temp filename may have changed during packing
-               if (csTempFileName != sIntermediateFilename)
-               {
-                       if (!::DeleteFile(sIntermediateFilename.c_str()))
-                       {
-                               LogErrorString(Fmt(_T("DeleteFile(%s) failed: %s"),
-                                       sIntermediateFilename.c_str(), GetSysError(GetLastError())));
-                       }
-                       sIntermediateFilename = csTempFileName;
-               }
-
-               // Write tempfile over original file
-               if (::CopyFile(sIntermediateFilename.c_str(), pszFileName, FALSE))
-               {
-                       if (!::DeleteFile(sIntermediateFilename.c_str()))
-                       {
-                               LogErrorString(Fmt(_T("DeleteFile(%s) failed: %s"),
-                                       sIntermediateFilename.c_str(), GetSysError(GetLastError())));
-                       }
-                       if (bClearModifiedFlag)
-                       {
-                               SetModified(FALSE);
-                               m_nSyncPosition = m_nUndoPosition;
-                       }
-                       bSaveSuccess = TRUE;
-
-                       // remember revision number on save
-                       m_dwRevisionNumberOnSave = m_dwCurrentRevisionNumber;
-
-                       // redraw line revision marks
-                       UpdateViews (NULL, NULL, UPDATE_FLAGSONLY);     
-               }
-               else
-               {
-                       sError = GetSysError(GetLastError());
-                       LogErrorString(Fmt(_T("CopyFile(%s, %s) failed: %s"),
-                               sIntermediateFilename.c_str(), pszFileName, sError));
-               }
-       }
-       else
-       {
-               if (bClearModifiedFlag)
-               {
-                       SetModified(FALSE);
-                       m_nSyncPosition = m_nUndoPosition;
-               }
-               bSaveSuccess = TRUE;
-       }
-
-       if (bSaveSuccess)
-               return SAVE_DONE;
-       else
-               return SAVE_FAILED;
-}
-
-/// Replace text of line (no change to eol)
-void CMergeDoc::CDiffTextBuffer::ReplaceLine(CCrystalTextView * pSource, int nLine, LPCTSTR pchText, int cchText, int nAction /*=CE_ACTION_UNKNOWN*/)
-{
-       if (GetLineLength(nLine)>0)
-               DeleteText(pSource, nLine, 0, nLine, GetLineLength(nLine), nAction);
-       int endl, endc;
-       if (cchText)
-               InsertText(pSource, nLine, 0, pchText, cchText, endl, endc, nAction);
-}
-
-/// Return pointer to the eol chars of this string, or pointer to empty string if none
-LPCTSTR getEol(const CString &str)
-{
-       if (str.GetLength()>1 && str[str.GetLength()-2]=='\r' && str[str.GetLength()-1]=='\n')
-               return (LPCTSTR)str + str.GetLength()-2;
-       if (str.GetLength()>0 && (str[str.GetLength()-1]=='\r' || str[str.GetLength()-1]=='\n'))
-               return (LPCTSTR)str + str.GetLength()-1;
-       return _T("");
-}
-
-/// Replace line (removing any eol, and only including one if in strText)
-void CMergeDoc::CDiffTextBuffer::ReplaceFullLine(CCrystalTextView * pSource, int nLine, const CString &strText, int nAction /*=CE_ACTION_UNKNOWN*/)
-{
-       if (_tcscmp(GetLineEol(nLine), getEol(strText)) == 0)
-       {
-               // (optimization) eols are the same, so just replace text inside line
-               // we must clean strText from its eol...
-               int eolLength = _tcslen(getEol(strText));
-               ReplaceLine(pSource, nLine, strText, strText.GetLength() - eolLength, nAction);
-               return;
-       }
-
-       // we may need a last line as the DeleteText end is (x=0,y=line+1)
-       if (nLine+1 == GetLineCount())
-               InsertGhostLine (pSource, GetLineCount());
-
-       if (GetFullLineLength(nLine))
-               DeleteText(pSource, nLine, 0, nLine+1, 0, nAction); 
-       int endl,endc;
-       if (int cchText = strText.GetLength())
-               InsertText(pSource, nLine, 0, strText, cchText, endl,endc, nAction);
-}
-
-/**
  * @brief Take care of rescanning document.
  * 
  * Update view and restore cursor and scroll position after
@@ -2279,12 +1555,6 @@ void CMergeDoc::OnUpdateStatusNum(CCmdUI* pCmdUI)
        pCmdUI->SetText(s.c_str());
 }
 
-bool CMergeDoc::CDiffTextBuffer::curUndoGroup()
-{
-       return (m_aUndoBuf.GetSize()!=0 && m_aUndoBuf[0].m_dwFlags&UNDO_BEGINGROUP);
-}
-
-
 /**
  * @brief Build the diff array and prepare buffers accordingly (insert ghost lines, set WinMerge flags)
  *
index a519389..3868b65 100644 (file)
@@ -28,7 +28,7 @@
 #if !defined(AFX_MERGEDOC_H__BBCD4F90_34E4_11D1_BAA6_00A024706EDC__INCLUDED_)
 #define AFX_MERGEDOC_H__BBCD4F90_34E4_11D1_BAA6_00A024706EDC__INCLUDED_
 
-#include "GhostTextBuffer.h"
+#include "DiffTextBuffer.h"
 #include <vector>
 #include "DiffWrapper.h"
 #include "DiffList.h"
@@ -122,87 +122,12 @@ class CMergeDoc : public CDocument
 {
 // Attributes
 public:
-
-/**
- * @brief Specialized buffer to save file data
- */
-class CDiffTextBuffer : public CGhostTextBuffer
-       {
-               friend class CMergeDoc;
-private :
-               CMergeDoc * m_pOwnerDoc; /**< Merge document owning this buffer. */
-               int m_nThisPane; /**< Left/Right side */
-               BOOL FlagIsSet(UINT line, DWORD flag);
-               String m_strTempPath; /**< Temporary files folder. */
-               int unpackerSubcode;
-               /* 
-                * @brief Unicode encoding from ucr::UNICODESET 
-                *
-                * @note m_unicoding and m_codepage are indications of how the buffer is supposed to be saved on disk
-                * In memory, it is invariant, depending on build:
-                * ANSI:
-                *   in memory it is CP_ACP/CP_THREAD_ACP 8-bit characters
-                * Unicode:
-                *   in memory it is wchars
-                */
-               FileTextEncoding m_encoding; /**< File's encoding information. */
-
-               int NoteCRLFStyleFromBuffer(TCHAR *lpLineBegin, DWORD dwLineLen = 0);
-               void ReadLineFromBuffer(TCHAR *lpLineBegin, DWORD dwLineNum, DWORD dwLineLen = 0);
-public :
-               void SetTempPath(String path);
-               virtual void AddUndoRecord (BOOL bInsert, const CPoint & ptStartPos, const CPoint & ptEndPos,
-                       LPCTSTR pszText, int cchText, int nLinesToValidate, int nActionType = CE_ACTION_UNKNOWN, CDWordArray *paSavedRevisonNumbers = NULL);
-               bool curUndoGroup();
-               void ReplaceLine(CCrystalTextView * pSource, int nLine, LPCTSTR pchText, int cchText, int nAction =CE_ACTION_UNKNOWN);
-               void ReplaceFullLine(CCrystalTextView * pSource, int nLine, const CString& strText, int nAction =CE_ACTION_UNKNOWN);
-
-               int LoadFromFile(LPCTSTR pszFileName, PackingInfo * infoUnpacker,
-                       LPCTSTR filteredFilenames, BOOL & readOnly, int nCrlfStyle,
-                       const FileTextEncoding & encoding, CString &sError);
-               int SaveToFile (LPCTSTR pszFileName, BOOL bTempFile, CString & sError,
-                       PackingInfo * infoUnpacker = NULL, int nCrlfStyle = CRLF_STYLE_AUTOMATIC,
-                       BOOL bClearModifiedFlag = TRUE );
-               int getUnicoding() const { return m_encoding.m_unicoding; }
-               void setUnicoding(int value) { m_encoding.m_unicoding = value; }
-               int getCodepage() const { return m_encoding.m_codepage; }
-               void setCodepage(int value) { m_encoding.m_codepage = value; }
-               const FileTextEncoding & getEncoding() const { return m_encoding; }
-
-               CDiffTextBuffer(CMergeDoc * pDoc, int pane);
-
-               // If line has text (excluding eol), set strLine to text (excluding eol)
-               BOOL GetLine(int nLineIndex, CString &strLine);
-
-               // if line has any text (including eol), set strLine to text (including eol)
-               BOOL GetFullLine(int nLineIndex, CString &strLine);
-
-               virtual void SetModified (BOOL bModified = TRUE);
-
-               void prepareForRescan();
-
-               /** 
-               After editing a line, we don't know if there is a diff or not.
-               So we clear the LF_DIFF flag (and it is more easy to read during edition).
-               Rescan will set the proper color
-               */
-               virtual void OnNotifyLineHasBeenEdited(int nLine);
-
-               bool IsInitialized() const;
-
-       } friend;
-
-// End declaration of CMergeDoc::CDiffTextBuffer
-
-// Begin declaration of CMergeDoc
-
        CDiffTextBuffer *m_ptBuf[2]; /**< Left/Right side text buffer */
 
 protected: // create from serialization only
        CMergeDoc();
        DECLARE_DYNCREATE(CMergeDoc)
 
-       
        // Operations
 public:        
        DiffFileInfo *m_pSaveFileInfo[2];
index 72bd44d..e24f97f 100644 (file)
@@ -704,7 +704,7 @@ void CMergeEditView::OnEditCopy()
 
        CString text;
 
-       CMergeDoc::CDiffTextBuffer * buffer = pDoc->m_ptBuf[m_nThisPane];
+       CDiffTextBuffer * buffer = pDoc->m_ptBuf[m_nThisPane];
 
        buffer->GetTextWithoutEmptys(ptSelStart.y, ptSelStart.x,
                ptSelEnd.y, ptSelEnd.x, text);
@@ -2957,7 +2957,7 @@ void CMergeEditView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
 bool CMergeEditView::IsInitialized() const
 {
        CMergeEditView * pThis = const_cast<CMergeEditView *>(this);
-       CMergeDoc::CDiffTextBuffer * pBuffer = dynamic_cast<CMergeDoc::CDiffTextBuffer *>(pThis->LocateTextBuffer());
+       CDiffTextBuffer * pBuffer = dynamic_cast<CDiffTextBuffer *>(pThis->LocateTextBuffer());
        return pBuffer->IsInitialized();
 }