view kdiff3/src/diff.h @ 67:ec82d69e8b0c

0.9.86
author joachim99
date Thu, 16 Sep 2004 02:45:37 +0000
parents efe33e938730
children d7cafcda8c99
line wrap: on
line source
/***************************************************************************
                          diff.h  -  description
                             -------------------
    begin                : Mon Mar 18 2002
    copyright            : (C) 2002-2004 by Joachim Eibl
    email                : joachim.eibl@gmx.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef DIFF_H
#define DIFF_H

#include <qwidget.h>
#include <qpixmap.h>
#include <qtimer.h>
#include <qframe.h>
#include <list>
#include <vector>
#include <assert.h>
#include "common.h"
#include "fileaccess.h"

class OptionDialog;

// Each range with matching elements is followed by a range with differences on either side.
// Then again range of matching elements should follow.
struct Diff
{
   int nofEquals;

   int diff1;
   int diff2;

   Diff(int eq, int d1, int d2){nofEquals=eq; diff1=d1; diff2=d2; }
};

typedef std::list<Diff> DiffList;

struct Diff3Line
{
   int lineA;
   int lineB;
   int lineC;

   bool bAEqC;                 // These are true if equal or only white-space changes exist.
   bool bBEqC;
   bool bAEqB;

   DiffList* pFineAB;          // These are 0 only if completely equal. 
   DiffList* pFineBC;
   DiffList* pFineCA;
   
   bool bWhiteLineA;
   bool bWhiteLineB;
   bool bWhiteLineC;

   int linesNeededForDisplay; // Due to wordwrap
   int sumLinesNeededForDisplay; // For fast conversion to m_diff3WrapLineVector
   
   Diff3Line()
   {
      lineA=-1; lineB=-1; lineC=-1;
      bAEqC=false; bAEqB=false; bBEqC=false;
      pFineAB=0; pFineBC=0; pFineCA=0;
      linesNeededForDisplay=1;
   }

   ~Diff3Line()
   {
      if (pFineAB!=0) delete pFineAB;
      if (pFineBC!=0) delete pFineBC;
      if (pFineCA!=0) delete pFineCA;
      pFineAB=0; pFineBC=0; pFineCA=0;
   }

   bool operator==( const Diff3Line& d3l )
   {
      return lineA == d3l.lineA  &&  lineB == d3l.lineB  &&  lineC == d3l.lineC  
         && bAEqB == d3l.bAEqB  && bAEqC == d3l.bAEqC  && bBEqC == d3l.bBEqC;
   }
};


typedef std::list<Diff3Line> Diff3LineList;
typedef std::vector<Diff3Line*> Diff3LineVector;

class Diff3WrapLine
{
public:
   Diff3Line* pD3L;
   int diff3LineIndex;
   int wrapLineOffset;
   int wrapLineLength;   
};

typedef std::vector<Diff3WrapLine> Diff3WrapLineVector;


class TotalDiffStatus
{
public:
   TotalDiffStatus(){ reset(); }
   void reset() {bBinaryAEqC=false; bBinaryBEqC=false; bBinaryAEqB=false;
                 bTextAEqC=false;   bTextBEqC=false;   bTextAEqB=false;
                 nofUnsolvedConflicts=0; nofSolvedConflicts=0;
                 nofWhitespaceConflicts=0;                 
                }
   bool bBinaryAEqC;
   bool bBinaryBEqC;
   bool bBinaryAEqB;

   bool bTextAEqC;
   bool bTextBEqC;
   bool bTextAEqB;
   
   int nofUnsolvedConflicts;
   int nofSolvedConflicts;
   int nofWhitespaceConflicts;
};

void calcDiff3LineListUsingAB(
   const DiffList* pDiffListAB,
   Diff3LineList& d3ll
   );

void calcDiff3LineListUsingAC(
   const DiffList* pDiffListBC,
   Diff3LineList& d3ll
   );

void calcDiff3LineListUsingBC(       
   const DiffList* pDiffListBC,
   Diff3LineList& d3ll
   );

struct LineData
{
   const char* pLine;
   const char* pFirstNonWhiteChar;
   int size;

   LineData(){ pLine=0; size=0; occurances=0; bContainsPureComment=false; }
   int width() const;  // Calcs width considering tabs.
   int occurances;
   bool whiteLine() const { return pFirstNonWhiteChar-pLine == size; }
   bool bContainsPureComment;
};


class SourceData
{
public:
   SourceData();
   ~SourceData();
   
   void setOptionDialog( OptionDialog* pOptionDialog );
   
   int getSizeLines() const;
   int getSizeBytes() const;
   const char* getBuf() const;
   const LineData* getLineDataForDisplay() const;
   const LineData* getLineDataForDiff() const;

   void setFilename(const QString& filename);
   void setFileAccess( const FileAccess& fa );
   FileAccess& getFileAccess();
   QString getFilename();
   void setAliasName(const QString& a);
   QString getAliasName();
   bool isEmpty();  // File was set
   bool hasData();  // Data was readable
   bool isText();   // is it pure text (vs. binary data)
   bool isFromBuffer();  // was it set via setData() (vs. setFileAccess() or setFilename())
   void setData( const QString& data );
   bool isValid(); // Either no file is specified or reading was successful
   
   void readAndPreprocess();
   bool saveNormalDataAs( const QString& fileName );
   
   bool isBinaryEqualWith( const SourceData& other ) const;
   
   void reset();

private:   
   QString m_aliasName;
   FileAccess m_fileAccess;
   OptionDialog* m_pOptionDialog;
   QString m_tempInputFileName;

   struct FileData
   {   
      FileData(){ m_pBuf=0; m_size=0; }
      ~FileData(){ reset(); }
      const char* m_pBuf;
      int m_size;
      int m_vSize; // Nr of lines in m_pBuf1 and size of m_v1, m_dv12 and m_dv13
      std::vector<LineData> m_v;
      bool m_bIsText;
      bool readFile( const QString& filename );
      bool writeFile( const QString& filename );
      void preprocess(bool bPreserveCR );
      void reset();
      void removeComments();
      void copyBufFrom( const FileData& src );
   };
   FileData m_normalData;
   FileData m_lmppData;   
};

void calcDiff3LineListTrim( Diff3LineList& d3ll, const LineData* pldA, const LineData* pldB, const LineData* pldC );
void calcWhiteDiff3Lines(   Diff3LineList& d3ll, const LineData* pldA, const LineData* pldB, const LineData* pldC );

void calcDiff3LineVector( Diff3LineList& d3ll, Diff3LineVector& d3lv );

void debugLineCheck( Diff3LineList& d3ll, int size, int idx );

class QStatusBar;



class Selection
{
public:
   Selection(){ reset(); oldLastLine=-1; lastLine=-1; oldFirstLine=-1; }
   int firstLine;
   int firstPos;
   int lastLine;
   int lastPos;
   int oldLastLine;
   int oldFirstLine;
   bool bSelectionContainsData;
   bool isEmpty() { return firstLine==-1 || (firstLine==lastLine && firstPos==lastPos) || bSelectionContainsData==false;}
   void reset(){
      oldFirstLine=firstLine;
      oldLastLine =lastLine;
      firstLine=-1;
      bSelectionContainsData = false;
   }
   void start( int l, int p ) { firstLine = l; firstPos = p; }
   void end( int l, int p )  {
      if ( oldLastLine == -1 )
         oldLastLine = lastLine;
      lastLine  = l;
      lastPos  = p;
   }
   bool within( int l, int p );

   bool lineWithin( int l );
   int firstPosInLine(int l);
   int lastPosInLine(int l);
   int beginLine(){ return min2(firstLine,lastLine); }
   int endLine(){ return max2(firstLine,lastLine); }
   int beginPos() { return firstLine==lastLine ? min2(firstPos,lastPos) :
                           firstLine<lastLine ? firstPos : lastPos;      }
   int endPos()   { return firstLine==lastLine ? max2(firstPos,lastPos) :
                           firstLine<lastLine ? lastPos : firstPos;      }
};

class OptionDialog;

QString decodeString( const char*s , OptionDialog* );
QCString encodeString( const QString& s , OptionDialog* );

class DiffTextWindow : public QWidget
{
   Q_OBJECT
public:
   DiffTextWindow(
      QWidget* pParent,
      QStatusBar* pStatusBar,
      OptionDialog* pOptionDialog
      );
   void init(
      const QString& fileName,
      const LineData* pLineData,
      int size,
      const Diff3LineVector* pDiff3LineVector,
      int winIdx,
      bool bTriple
      );
   virtual void mousePressEvent ( QMouseEvent * );
   virtual void mouseReleaseEvent ( QMouseEvent * );
   virtual void mouseMoveEvent ( QMouseEvent * );
   virtual void mouseDoubleClickEvent ( QMouseEvent * e );
   void convertToLinePos( int x, int y, int& line, int& pos );

   virtual void paintEvent( QPaintEvent*  );
   virtual void dragEnterEvent( QDragEnterEvent* e );
   virtual void focusInEvent( QFocusEvent* e );
   //void setData( const char* pText);

   virtual void resizeEvent( QResizeEvent* );

   QString getSelection();
   int getFirstLine() { return m_firstLine; }

   int getNofColumns();
   int getNofLines();
   int getNofVisibleLines();
   int getNofVisibleColumns();
   
   int convertLineToDiff3LineIdx( int line );
   int convertDiff3LineIdxToLine( int d3lIdx );
   
   void convertD3LCoordsToLineCoords( int d3LIdx, int d3LPos, int& line, int& pos );
   void convertLineCoordsToD3LCoords( int line, int pos, int& d3LIdx, int& d3LPos );

   void convertSelectionToD3LCoords();
   
   bool findString( const QCString& s, int& d3vLine, int& posInLine, bool bDirDown, bool bCaseSensitive );
   void setSelection( int firstLine, int startPos, int lastLine, int endPos, int& l, int& p );

   void setPaintingAllowed( bool bAllowPainting );
   void recalcWordWrap( bool bWordWrap, int wrapLineVectorSize );
signals:
   void resizeSignal( int nofVisibleColumns, int nofVisibleLines );
   void scroll( int deltaX, int deltaY );
   void newSelection();
   void selectionEnd();
   void setFastSelectorLine( int line );
   void gotFocus();

public slots:
   void setFirstLine( int line );
   void setFirstColumn( int col );
   void resetSelection();
   void setFastSelectorRange( int line1, int nofLines );

private:
   bool m_bPaintingAllowed;
   const LineData* m_pLineData;
   int m_size;
   QString m_filename;
   bool m_bWordWrap;

   const Diff3LineVector* m_pDiff3LineVector;
   Diff3WrapLineVector m_diff3WrapLineVector;

   OptionDialog* m_pOptionDialog;
   QColor m_cThis;
   QColor m_cDiff1;
   QColor m_cDiff2;
   QColor m_cDiffBoth;

   int m_fastSelectorLine1;
   int m_fastSelectorNofLines;

   bool m_bTriple;
   int m_winIdx;
   int m_firstLine;
   int m_oldFirstLine;
   int m_oldFirstColumn;
   int m_firstColumn;
   int m_lineNumberWidth;

   void getLineInfo(
      const Diff3Line& d,
      int& lineIdx,
      DiffList*& pFineDiff1, DiffList*& pFineDiff2,   // return values
      int& changed, int& changed2  );

   QCString getString( int d3lIdx );
   QCString getLineString( int line );

   void writeLine(
      QPainter& p, const LineData* pld,
      const DiffList* pLineDiff1, const DiffList* pLineDiff2, int line,
      int whatChanged, int whatChanged2, int srcLineNr,
      int wrapLineOffset, int wrapLineLength, bool bWrapLine );

   QStatusBar* m_pStatusBar;

   Selection selection;

   int m_scrollDeltaX;
   int m_scrollDeltaY;
   virtual void timerEvent(QTimerEvent*);
   bool m_bMyUpdate;
   void myUpdate(int afterMilliSecs );
   
   void showStatusLine( int line );
   
   QRect m_invalidRect;
};



class Overview : public QWidget
{
   Q_OBJECT
public:
   Overview( QWidget* pParent, OptionDialog* pOptions );
             
   void init( Diff3LineList* pDiff3LineList, bool bTripleDiff );
   void setRange( int firstLine, int pageHeight );
   void setPaintingAllowed( bool bAllowPainting );
   
   enum e_OverviewMode { eOMNormal, eOMAvsB, eOMAvsC, eOMBvsC };
   void setOverviewMode( e_OverviewMode eOverviewMode );
   e_OverviewMode getOverviewMode();

public slots:
   void setFirstLine(int firstLine);
   void slotRedraw();
signals:
   void setLine(int);
private:
   const Diff3LineList* m_pDiff3LineList;
   OptionDialog* m_pOptions;
   bool m_bTripleDiff;
   int m_firstLine;
   int m_pageHeight;
   QPixmap m_pixmap;
   bool m_bPaintingAllowed;
   e_OverviewMode m_eOverviewMode;
   int m_nofLines;

   virtual void paintEvent( QPaintEvent* e );
   virtual void mousePressEvent( QMouseEvent* e );
   virtual void mouseMoveEvent( QMouseEvent* e );
   void drawColumn( QPainter& p, e_OverviewMode eOverviewMode, int x, int w, int h, int nofLines );
};


enum e_MergeDetails
{
   eDefault,
   eNoChange,
   eBChanged,
   eCChanged,
   eBCChanged,         // conflict
   eBCChangedAndEqual, // possible conflict
   eBDeleted,
   eCDeleted,
   eBCDeleted,         // possible conflict

   eBChanged_CDeleted, // conflict
   eCChanged_BDeleted, // conflict
   eBAdded,
   eCAdded,
   eBCAdded,           // conflict
   eBCAddedAndEqual    // possible conflict
};

void mergeOneLine( const Diff3Line& d, e_MergeDetails& mergeDetails, bool& bConflict, bool& bLineRemoved, int& src, bool bTwoInputs );

enum e_MergeSrcSelector
{
   A=1,
   B=2,
   C=3
};

class MergeResultWindow : public QWidget
{
   Q_OBJECT
public:
   MergeResultWindow(
      QWidget* pParent,
      OptionDialog* pOptionDialog,
      QStatusBar* pStatusBar
      );

   void init(
      const LineData* pLineDataA,
      const LineData* pLineDataB,
      const LineData* pLineDataC,
      const Diff3LineList* pDiff3LineList,
      TotalDiffStatus* pTotalDiffStatus,
      QString fileName
      );

   bool saveDocument( const QString& fileName );
   int getNrOfUnsolvedConflicts();
   void choose(int selector);
   void chooseGlobal(int selector, bool bConflictsOnly, bool bWhiteSpaceOnly );

   int getNofColumns();
   int getNofLines();
   int getNofVisibleColumns();
   int getNofVisibleLines();
   QString getSelection();
   void resetSelection();
   void showNrOfConflicts();
   bool isDeltaAboveCurrent();
   bool isDeltaBelowCurrent();
   bool isConflictAboveCurrent();
   bool isConflictBelowCurrent();
   bool isUnsolvedConflictAboveCurrent();
   bool isUnsolvedConflictBelowCurrent();
   bool findString( const QCString& s, int& d3vLine, int& posInLine, bool bDirDown, bool bCaseSensitive );
   void setSelection( int firstLine, int startPos, int lastLine, int endPos );
public slots:
   void setFirstLine(int firstLine);
   void setFirstColumn(int firstCol);

   void slotGoCurrent();
   void slotGoTop();
   void slotGoBottom();
   void slotGoPrevDelta();
   void slotGoNextDelta();
   void slotGoPrevUnsolvedConflict();
   void slotGoNextUnsolvedConflict();
   void slotGoPrevConflict();
   void slotGoNextConflict();
   void slotAutoSolve();
   void slotUnsolve();
   void slotSetFastSelectorLine(int);
   void setPaintingAllowed(bool);
   void updateSourceMask();

signals:
   void scroll( int deltaX, int deltaY );
   void modified();
   void setFastSelectorRange( int line1, int nofLines );
   void sourceMask( int srcMask, int enabledMask );   
   void resizeSignal();
   void selectionEnd();
   void newSelection();
   void updateAvailabilities();
   void showPopupMenu( const QPoint& point );

private:
   void merge(bool bAutoSolve, int defaultSelector, bool bConflictsOnly=false, bool bWhiteSpaceOnly=false );
   QCString getString( int lineIdx );

   OptionDialog* m_pOptionDialog;

   const LineData* m_pldA;
   const LineData* m_pldB;
   const LineData* m_pldC;

   const Diff3LineList* m_pDiff3LineList;
   TotalDiffStatus* m_pTotalDiffStatus;

   bool m_bPaintingAllowed;

private:
   class MergeEditLine
   {
   public:
      MergeEditLine(){ m_src=0; m_bLineRemoved=false; }
      void setConflict() { m_src=0; m_bLineRemoved=false; m_str=QCString(); }
      bool isConflict()  { return  m_src==0 && !m_bLineRemoved && m_str.isNull(); }
      void setRemoved(int src=0)  { m_src=src; m_bLineRemoved=true; m_str=QCString(); }
      bool isRemoved()   { return m_bLineRemoved; }
      bool isEditableText() { return !isConflict() && !isRemoved(); }
      void setString( const QCString& s ){ m_str=s; m_bLineRemoved=false; m_src=0; }
      const char* getString( const MergeResultWindow*, int& size );
      bool isModified() { return ! m_str.isNull() ||  (m_bLineRemoved && m_src==0); }

      void setSource( int src, Diff3LineList::const_iterator i, bool bLineRemoved )
      {
         m_src=src; m_id3l=i; m_bLineRemoved =bLineRemoved;
      }
      int src() { return m_src; }
      Diff3LineList::const_iterator id3l(){return m_id3l;}
      // getString() is implemented as MergeResultWindow::getString()
   private:
      Diff3LineList::const_iterator m_id3l;
      int m_src;         // 1, 2 or 3 for A, B or C respectively, or 0 when line is from neither source.
      QCString m_str;    // String when modified by user or null-string when orig data is used.
      bool m_bLineRemoved;
   };

   class MergeEditLineList : private std::list<MergeEditLine>
   { // I want to know the size immediately!
   private:
      typedef std::list<MergeEditLine> BASE;
      int m_size;
      int* m_pTotalSize;
   public:
      typedef std::list<MergeEditLine>::iterator iterator;
      typedef std::list<MergeEditLine>::const_iterator const_iterator;
      MergeEditLineList(){m_size=0; m_pTotalSize=0; }
      void clear()                             { ds(-m_size); BASE::clear();          }
      void push_back( const MergeEditLine& m)  { ds(+1); BASE::push_back(m);     }
      void push_front( const MergeEditLine& m) { ds(+1); BASE::push_front(m);    }
      iterator erase( iterator i )             { ds(-1); return BASE::erase(i);  }
      iterator insert( iterator i, const MergeEditLine& m ) { ds(+1); return BASE::insert(i,m); }
      int size(){ /*assert(int(BASE::size())==m_size);*/ return m_size;}
      iterator begin(){return BASE::begin();}
      iterator end(){return BASE::end();}
      bool empty() { return m_size==0; }
      
      void setTotalSizePtr(int* pTotalSize)
      {
         m_pTotalSize = pTotalSize;
         *m_pTotalSize += m_size;
      }
      
   private:
      void ds(int deltaSize) 
      { 
         m_size+=deltaSize; 
         if (m_pTotalSize!=0)  *m_pTotalSize+=deltaSize;
      }
   };

   friend class MergeEditLine;

   struct MergeLine
   {
      MergeLine()
      {
         srcSelect=0; mergeDetails=eDefault; d3lLineIdx = -1; srcRangeLength=0;
         bConflict=false; bDelta=false; bWhiteSpaceConflict=false;
      }
      Diff3LineList::const_iterator id3l;
      e_MergeDetails mergeDetails;
      int d3lLineIdx;  // Needed to show the correct window pos.
      int srcRangeLength; // how many src-lines have this properties
      bool bConflict;
      bool bWhiteSpaceConflict;
      bool bDelta;
      int srcSelect;
      MergeEditLineList mergeEditLineList;
   };

private:
   static bool sameKindCheck( const MergeLine& ml1, const MergeLine& ml2 );

   typedef std::list<MergeLine> MergeLineList;
   MergeLineList m_mergeLineList;
   MergeLineList::iterator m_currentMergeLineIt;
   int m_currentPos;

   enum e_Direction { eUp, eDown };
   enum e_EndPoint  { eDelta, eConflict, eUnsolvedConflict, eLine, eEnd };
   void go( e_Direction eDir, e_EndPoint eEndPoint );
   void calcIteratorFromLineNr(
      int line,
      MergeLineList::iterator& mlIt,
      MergeEditLineList::iterator& melIt
      );

   virtual void paintEvent( QPaintEvent* e );


   void myUpdate(int afterMilliSecs);
   virtual void timerEvent(QTimerEvent*);
   void writeLine(
      QPainter& p, int line, const char* pStr, int size,
      int srcSelect, e_MergeDetails mergeDetails, int rangeMark, bool bUserModified, bool bLineRemoved
      );
   void setFastSelector(MergeLineList::iterator i);
   void convertToLinePos( int x, int y, int& line, int& pos );
   virtual void mousePressEvent ( QMouseEvent* e );
   virtual void mouseDoubleClickEvent ( QMouseEvent* e );
   virtual void mouseReleaseEvent ( QMouseEvent * );
   virtual void mouseMoveEvent ( QMouseEvent * );
   virtual void resizeEvent( QResizeEvent* e );
   virtual void keyPressEvent( QKeyEvent* e );
   virtual void wheelEvent( QWheelEvent* e );
   virtual void focusInEvent( QFocusEvent* e );

   QPixmap m_pixmap;
   int m_firstLine;
   int m_firstColumn;
   int m_nofColumns;
   int m_nofLines;
   int m_totalSize; //Same as m_nofLines, but calculated differently
   bool m_bMyUpdate;
   bool m_bInsertMode;
   QString m_fileName;
   bool m_bModified;
   void setModified();

   int m_scrollDeltaX;
   int m_scrollDeltaY;
   int m_cursorXPos;
   int m_cursorYPos;
   int m_cursorOldXPos;
   bool m_bCursorOn; // blinking on and off each second
   QTimer m_cursorTimer;
   QStatusBar* m_pStatusBar;
   
   Selection m_selection;

   bool deleteSelection2( const char*& ps, int& stringLength, int& x, int& y,
                    MergeLineList::iterator& mlIt, MergeEditLineList::iterator& melIt );
public slots:
   void deleteSelection();
   void pasteClipboard(bool bFromSelection);
private slots:
   void slotCursorUpdate();
};

void fineDiff(
   Diff3LineList& diff3LineList,
   int selector,
   const LineData* v1,
   const LineData* v2,
   bool& bTextsTotalEqual
   );


bool equal( const LineData& l1, const LineData& l2, bool bStrict );




inline bool isWhite( char c )
{
   return c==' ' || c=='\t' ||  c=='\r';
}

/** Returns the number of equivalent spaces at position outPos.
*/
inline int tabber( int outPos, int tabSize )
{
   return tabSize - ( outPos % tabSize );
}

/** Returns a line number where the linerange [line, line+nofLines] can
    be displayed best. If it fits into the currently visible range then
    the returned value is the current firstLine.
*/
int getBestFirstLine( int line, int nofLines, int firstLine, int visibleLines );

extern int g_tabSize;
extern bool g_bIgnoreWhiteSpace;
extern bool g_bIgnoreTrivialMatches;
extern int g_bAutoSolve;

// Cursor conversions that consider g_tabSize.
int convertToPosInText( const char* p, int size, int posOnScreen );
int convertToPosOnScreen( const QString& p, int posInText );
void calcTokenPos( const char* p, int size, int posOnScreen, int& pos1, int& pos2 );
#endif