2013-02-15 12:49:50 +01:00
/****************************************************************************
* *
2014-01-07 13:27:11 +01:00
* * Copyright ( C ) 2014 Digia Plc and / or its subsidiary ( - ies ) .
2013-02-15 12:49:50 +01:00
* * Contact : http : //www.qt-project.org/legal
* *
* * This file is part of Qt Creator .
* *
* * Commercial License Usage
* * Licensees holding valid commercial Qt licenses may use this file in
* * accordance with the commercial license agreement provided with the
* * Software or , alternatively , in accordance with the terms contained in
* * a written agreement between you and Digia . For licensing terms and
* * conditions see http : //qt.digia.com/licensing. For further information
* * use the contact form at http : //qt.digia.com/contact-us.
* *
* * GNU Lesser General Public License Usage
* * Alternatively , this file may be used under the terms of the GNU Lesser
* * General Public License version 2.1 as published by the Free Software
* * Foundation and appearing in the file LICENSE . LGPL included in the
* * packaging of this file . Please review the following information to
* * ensure the GNU Lesser General Public License version 2.1 requirements
* * will be met : http : //www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
* *
* * In addition , as a special exception , Digia gives you certain additional
* * rights . These rights are described in the Digia Qt LGPL Exception
* * version 1.1 , included in the file LGPL_EXCEPTION . txt in this package .
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "diffeditorwidget.h"
# include <QPlainTextEdit>
# include <QVBoxLayout>
# include <QPlainTextDocumentLayout>
# include <QTextBlock>
# include <QScrollBar>
# include <QPainter>
2013-05-07 14:02:08 +02:00
# include <QDir>
2013-05-22 16:33:44 +02:00
# include <QToolButton>
2013-02-15 12:49:50 +01:00
# include <texteditor/basetexteditor.h>
# include <texteditor/basetextdocumentlayout.h>
2013-08-14 13:52:13 +02:00
# include <texteditor/ihighlighterfactory.h>
2013-02-15 12:49:50 +01:00
# include <texteditor/syntaxhighlighter.h>
# include <texteditor/basetextdocument.h>
# include <texteditor/texteditorsettings.h>
2013-06-05 15:25:42 +02:00
# include <texteditor/fontsettings.h>
2013-06-03 15:17:34 +02:00
# include <texteditor/displaysettings.h>
2013-09-13 12:13:10 +02:00
# include <texteditor/highlighterutils.h>
2013-02-15 12:49:50 +01:00
2013-08-14 13:52:13 +02:00
# include <coreplugin/icore.h>
2013-05-24 13:22:46 +02:00
# include <coreplugin/minisplitter.h>
2013-08-14 13:52:13 +02:00
# include <coreplugin/mimedatabase.h>
# include <extensionsystem/pluginmanager.h>
2013-05-24 13:22:46 +02:00
2013-06-18 14:09:54 +02:00
# include <utils/tooltip/tipcontents.h>
# include <utils/tooltip/tooltip.h>
2013-04-25 17:37:20 +02:00
static const int FILE_LEVEL = 1 ;
static const int CHUNK_LEVEL = 2 ;
2013-08-14 13:52:13 +02:00
using namespace Core ;
2013-02-15 12:49:50 +01:00
using namespace TextEditor ;
2013-02-21 17:03:00 +01:00
namespace DiffEditor {
2013-02-15 12:49:50 +01:00
2013-12-16 16:19:40 +01:00
class TextLineData {
public :
2013-05-07 14:02:08 +02:00
enum TextLineType {
TextLine ,
Separator ,
Invalid
} ;
TextLineData ( ) : textLineType ( Invalid ) { }
TextLineData ( const QString & txt ) : textLineType ( TextLine ) , text ( txt ) { }
TextLineData ( TextLineType t ) : textLineType ( t ) { }
TextLineType textLineType ;
QString text ;
} ;
2013-12-16 16:19:40 +01:00
class RowData {
public :
2013-05-07 14:02:08 +02:00
RowData ( ) : equal ( true ) { }
RowData ( const TextLineData & l )
: leftLine ( l ) , rightLine ( l ) , equal ( true ) { }
RowData ( const TextLineData & l , const TextLineData & r , bool e = false )
: leftLine ( l ) , rightLine ( r ) , equal ( e ) { }
TextLineData leftLine ;
TextLineData rightLine ;
bool equal ; // true if left and right lines are equal, taking whitespaces into account (or both invalid)
} ;
2013-12-16 16:19:40 +01:00
class ChunkData {
public :
2013-05-07 14:02:08 +02:00
ChunkData ( ) : contextChunk ( false ) { }
QList < RowData > rows ;
bool contextChunk ;
QMap < int , int > changedLeftPositions ; // counting from the beginning of the chunk
QMap < int , int > changedRightPositions ; // counting from the beginning of the chunk
} ;
2013-12-16 16:19:40 +01:00
class FileData {
public :
2013-05-07 14:02:08 +02:00
FileData ( ) { }
FileData ( const ChunkData & chunkData ) { chunks . append ( chunkData ) ; }
QList < ChunkData > chunks ;
2013-12-16 16:19:40 +01:00
DiffEditorController : : DiffFileInfo leftFileInfo ;
DiffEditorController : : DiffFileInfo rightFileInfo ;
2013-05-07 14:02:08 +02:00
} ;
2013-02-15 12:49:50 +01:00
//////////////////////
2013-02-22 11:32:13 +01:00
class DiffViewEditorEditable : public BaseTextEditor
2013-02-15 12:49:50 +01:00
{
2013-08-14 13:52:13 +02:00
Q_OBJECT
2013-02-15 12:49:50 +01:00
public :
2013-06-18 14:09:54 +02:00
DiffViewEditorEditable ( BaseTextEditorWidget * editorWidget )
: BaseTextEditor ( editorWidget )
{
connect ( this , SIGNAL ( tooltipRequested ( TextEditor : : ITextEditor * , QPoint , int ) ) ,
2013-10-10 17:41:34 +02:00
this , SLOT ( slotTooltipRequested ( TextEditor : : ITextEditor * , QPoint , int ) ) ) ;
2013-06-18 14:09:54 +02:00
}
2013-02-15 12:49:50 +01:00
2013-05-27 13:12:44 +02:00
Core : : Id id ( ) const { return " DiffViewEditor " ; }
2013-06-18 14:09:54 +02:00
private slots :
void slotTooltipRequested ( TextEditor : : ITextEditor * editor , const QPoint & globalPoint , int position ) ;
2013-02-15 12:49:50 +01:00
} ;
2013-08-14 13:52:13 +02:00
class MultiHighlighter : public SyntaxHighlighter
{
Q_OBJECT
public :
MultiHighlighter ( DiffViewEditorWidget * editor , QTextDocument * document = 0 ) ;
~ MultiHighlighter ( ) ;
virtual void setFontSettings ( const TextEditor : : FontSettings & fontSettings ) ;
2013-12-16 16:19:40 +01:00
void setDocuments ( const QList < QPair < DiffEditorController : : DiffFileInfo , QString > > & documents ) ;
2013-08-14 13:52:13 +02:00
protected :
virtual void highlightBlock ( const QString & text ) ;
private :
DiffViewEditorWidget * m_editor ;
QMap < QString , IHighlighterFactory * > m_mimeTypeToHighlighterFactory ;
QList < SyntaxHighlighter * > m_highlighters ;
QList < QTextDocument * > m_documents ;
} ;
2013-04-09 10:26:31 +02:00
2013-02-15 12:49:50 +01:00
////////////////////////
2013-08-14 13:52:13 +02:00
class DiffViewEditorWidget : public BaseTextEditorWidget
2013-02-15 12:49:50 +01:00
{
Q_OBJECT
public :
2013-12-16 16:19:40 +01:00
class ExtendedFileInfo {
public :
DiffEditorController : : DiffFileInfo fileInfo ;
2013-08-14 13:52:13 +02:00
TextEditor : : SyntaxHighlighter * highlighter ;
} ;
2013-02-15 12:49:50 +01:00
DiffViewEditorWidget ( QWidget * parent = 0 ) ;
2013-08-14 13:52:13 +02:00
// TODO: remove me, codec should be taken from somewhere else
2013-02-15 12:49:50 +01:00
QTextCodec * codec ( ) const {
return const_cast < QTextCodec * > ( baseTextDocument ( ) - > codec ( ) ) ;
}
2013-08-14 13:52:13 +02:00
// block number, file info
2013-12-16 16:19:40 +01:00
QMap < int , DiffEditorController : : DiffFileInfo > fileInfo ( ) const { return m_fileInfo ; }
2013-02-15 12:49:50 +01:00
2013-04-26 11:50:10 +02:00
void setLineNumber ( int blockNumber , int lineNumber ) ;
2013-12-16 16:19:40 +01:00
void setFileInfo ( int blockNumber , const DiffEditorController : : DiffFileInfo & fileInfo ) ;
2013-04-25 17:37:20 +02:00
void setSkippedLines ( int blockNumber , int skippedLines ) { m_skippedLines [ blockNumber ] = skippedLines ; setSeparator ( blockNumber , true ) ; }
2013-04-09 10:26:31 +02:00
void setSeparator ( int blockNumber , bool separator ) { m_separators [ blockNumber ] = separator ; }
2013-05-07 14:02:08 +02:00
bool isFileLine ( int blockNumber ) const { return m_fileInfo . contains ( blockNumber ) ; }
2013-05-23 13:36:27 +02:00
int blockNumberForFileIndex ( int fileIndex ) const ;
int fileIndexForBlockNumber ( int blockNumber ) const ;
2013-04-25 17:37:20 +02:00
bool isChunkLine ( int blockNumber ) const { return m_skippedLines . contains ( blockNumber ) ; }
2013-05-07 14:02:08 +02:00
void clearAll ( const QString & message ) ;
2013-04-25 17:37:20 +02:00
void clearAllData ( ) ;
2013-08-14 13:52:13 +02:00
QTextBlock firstVisibleBlock ( ) const { return BaseTextEditorWidget : : firstVisibleBlock ( ) ; }
2013-12-16 16:19:40 +01:00
void setDocuments ( const QList < QPair < DiffEditorController : : DiffFileInfo , QString > > & documents ) ;
2013-02-15 12:49:50 +01:00
2013-06-03 15:17:34 +02:00
public slots :
void setDisplaySettings ( const DisplaySettings & ds ) ;
2013-07-01 13:15:27 +02:00
signals :
void jumpToOriginalFileRequested ( int diffFileIndex ,
int lineNumber ,
int columnNumber ) ;
2013-02-15 12:49:50 +01:00
protected :
virtual int extraAreaWidth ( int * markWidthPtr = 0 ) const { return BaseTextEditorWidget : : extraAreaWidth ( markWidthPtr ) ; }
2014-01-21 11:26:39 +01:00
void applyFontSettings ( ) ;
2013-02-22 11:32:13 +01:00
BaseTextEditor * createEditor ( ) { return new DiffViewEditorEditable ( this ) ; }
2013-02-15 12:49:50 +01:00
virtual QString lineNumber ( int blockNumber ) const ;
virtual int lineNumberDigits ( ) const ;
2013-04-09 10:26:31 +02:00
virtual bool selectionVisible ( int blockNumber ) const ;
2013-04-10 13:51:04 +02:00
virtual bool replacementVisible ( int blockNumber ) const ;
2013-06-05 15:25:42 +02:00
QColor replacementPenColor ( int blockNumber ) const ;
2013-04-25 17:37:20 +02:00
virtual QString plainTextFromSelection ( const QTextCursor & cursor ) const ;
virtual void drawCollapsedBlockPopup ( QPainter & painter ,
const QTextBlock & block ,
QPointF offset ,
const QRect & clip ) ;
2013-04-26 11:50:10 +02:00
void mouseDoubleClickEvent ( QMouseEvent * e ) ;
2013-02-15 12:49:50 +01:00
virtual void paintEvent ( QPaintEvent * e ) ;
virtual void scrollContentsBy ( int dx , int dy ) ;
private :
2013-04-25 17:37:20 +02:00
void paintCollapsedBlockPopup ( QPainter & painter , const QRect & clipRect ) ;
2013-06-05 15:25:42 +02:00
void paintSeparator ( QPainter & painter , QColor & color , const QString & text ,
const QTextBlock & block , int top ) ;
2013-04-26 11:50:10 +02:00
void jumpToOriginalFile ( const QTextCursor & cursor ) ;
2013-04-25 17:37:20 +02:00
2013-08-14 13:52:13 +02:00
// block number, visual line number.
2013-04-26 11:50:10 +02:00
QMap < int , int > m_lineNumbers ;
2013-02-15 12:49:50 +01:00
int m_lineNumberDigits ;
2013-08-14 13:52:13 +02:00
// block number, fileInfo. Set for file lines only.
2013-12-16 16:19:40 +01:00
QMap < int , DiffEditorController : : DiffFileInfo > m_fileInfo ;
2013-08-14 13:52:13 +02:00
// block number, skipped lines. Set for chunk lines only.
2013-04-09 10:26:31 +02:00
QMap < int , int > m_skippedLines ;
2013-08-14 13:52:13 +02:00
// block number, separator. Set for file, chunk or span line.
2013-04-09 10:26:31 +02:00
QMap < int , bool > m_separators ;
2013-04-25 17:37:20 +02:00
bool m_inPaintEvent ;
2013-06-05 15:25:42 +02:00
QColor m_fileLineForeground ;
QColor m_chunkLineForeground ;
QColor m_textForeground ;
2013-08-14 13:52:13 +02:00
MultiHighlighter * m_highlighter ;
2013-02-15 12:49:50 +01:00
} ;
2013-08-14 13:52:13 +02:00
MultiHighlighter : : MultiHighlighter ( DiffViewEditorWidget * editor , QTextDocument * document )
: SyntaxHighlighter ( document ) ,
m_editor ( editor )
{
const QList < IHighlighterFactory * > & factories =
ExtensionSystem : : PluginManager : : getObjects < TextEditor : : IHighlighterFactory > ( ) ;
foreach ( IHighlighterFactory * factory , factories ) {
QStringList mimeTypes = factory - > mimeTypes ( ) ;
foreach ( const QString & mimeType , mimeTypes )
m_mimeTypeToHighlighterFactory . insert ( mimeType , factory ) ;
}
}
MultiHighlighter : : ~ MultiHighlighter ( )
{
2013-12-16 16:19:40 +01:00
setDocuments ( QList < QPair < DiffEditorController : : DiffFileInfo , QString > > ( ) ) ;
2013-08-14 13:52:13 +02:00
}
void MultiHighlighter : : setFontSettings ( const TextEditor : : FontSettings & fontSettings )
{
foreach ( SyntaxHighlighter * highlighter , m_highlighters ) {
if ( highlighter ) {
highlighter - > setFontSettings ( fontSettings ) ;
highlighter - > rehighlight ( ) ;
}
}
}
2013-12-16 16:19:40 +01:00
void MultiHighlighter : : setDocuments ( const QList < QPair < DiffEditorController : : DiffFileInfo , QString > > & documents )
2013-08-14 13:52:13 +02:00
{
// clear old documents
qDeleteAll ( m_documents ) ;
m_documents . clear ( ) ;
qDeleteAll ( m_highlighters ) ;
m_highlighters . clear ( ) ;
// create new documents
for ( int i = 0 ; i < documents . count ( ) ; i + + ) {
2013-12-16 16:19:40 +01:00
DiffEditorController : : DiffFileInfo fileInfo = documents . at ( i ) . first ;
2013-08-14 13:52:13 +02:00
const QString contents = documents . at ( i ) . second ;
QTextDocument * document = new QTextDocument ( contents ) ;
2013-08-30 16:38:57 +02:00
const MimeType mimeType = MimeDatabase : : findByFile ( QFileInfo ( fileInfo . fileName ) ) ;
2013-08-14 13:52:13 +02:00
SyntaxHighlighter * highlighter = 0 ;
if ( const IHighlighterFactory * factory = m_mimeTypeToHighlighterFactory . value ( mimeType . type ( ) ) ) {
highlighter = factory - > createHighlighter ( ) ;
if ( highlighter )
highlighter - > setDocument ( document ) ;
}
if ( ! highlighter ) {
2013-09-13 12:13:10 +02:00
highlighter = createGenericSyntaxHighlighter ( mimeType ) ;
2013-08-14 13:52:13 +02:00
highlighter - > setDocument ( document ) ;
}
m_documents . append ( document ) ;
m_highlighters . append ( highlighter ) ;
}
}
void MultiHighlighter : : highlightBlock ( const QString & text )
{
Q_UNUSED ( text )
QTextBlock block = currentBlock ( ) ;
const int fileIndex = m_editor - > fileIndexForBlockNumber ( block . blockNumber ( ) ) ;
if ( fileIndex < 0 )
return ;
SyntaxHighlighter * currentHighlighter = m_highlighters . at ( fileIndex ) ;
if ( ! currentHighlighter )
return ;
// find block in document
QTextDocument * currentDocument = m_documents . at ( fileIndex ) ;
if ( ! currentDocument )
return ;
QTextBlock documentBlock = currentDocument - > findBlockByNumber (
block . blockNumber ( ) - m_editor - > blockNumberForFileIndex ( fileIndex ) ) ;
QList < QTextLayout : : FormatRange > formats = documentBlock . layout ( ) - > additionalFormats ( ) ;
setExtraAdditionalFormats ( block , formats ) ;
}
2013-06-18 14:09:54 +02:00
void DiffViewEditorEditable : : slotTooltipRequested ( TextEditor : : ITextEditor * editor , const QPoint & globalPoint , int position )
{
DiffViewEditorWidget * ew = qobject_cast < DiffViewEditorWidget * > ( editorWidget ( ) ) ;
if ( ! ew )
return ;
2013-12-16 16:19:40 +01:00
QMap < int , DiffEditorController : : DiffFileInfo > fi = ew - > fileInfo ( ) ;
QMap < int , DiffEditorController : : DiffFileInfo > : : const_iterator it
2013-06-18 14:09:54 +02:00
= fi . constFind ( ew - > document ( ) - > findBlock ( position ) . blockNumber ( ) ) ;
if ( it ! = fi . constEnd ( ) ) {
2013-09-11 17:11:15 +02:00
Utils : : ToolTip : : show ( globalPoint , Utils : : TextContent ( it . value ( ) . fileName ) ,
2013-06-18 14:09:54 +02:00
editor - > widget ( ) ) ;
} else {
2013-09-11 17:11:15 +02:00
Utils : : ToolTip : : hide ( ) ;
2013-06-18 14:09:54 +02:00
}
}
2013-02-15 12:49:50 +01:00
DiffViewEditorWidget : : DiffViewEditorWidget ( QWidget * parent )
2013-08-14 13:52:13 +02:00
: BaseTextEditorWidget ( parent ) , m_lineNumberDigits ( 1 ) , m_inPaintEvent ( false )
2013-02-15 12:49:50 +01:00
{
2013-06-03 15:17:34 +02:00
DisplaySettings settings = displaySettings ( ) ;
settings . m_textWrapping = false ;
settings . m_displayLineNumbers = true ;
settings . m_highlightCurrentLine = false ;
settings . m_displayFoldingMarkers = true ;
settings . m_markTextChanges = false ;
settings . m_highlightBlocks = false ;
2013-08-14 13:52:13 +02:00
BaseTextEditorWidget : : setDisplaySettings ( settings ) ;
2013-06-03 15:17:34 +02:00
2013-04-10 13:51:04 +02:00
setCodeFoldingSupported ( true ) ;
2013-02-15 12:49:50 +01:00
setFrameStyle ( QFrame : : NoFrame ) ;
2013-08-14 13:52:13 +02:00
m_highlighter = new MultiHighlighter ( this , baseTextDocument ( ) - > document ( ) ) ;
baseTextDocument ( ) - > setSyntaxHighlighter ( m_highlighter ) ;
2013-02-15 12:49:50 +01:00
}
2013-06-03 15:17:34 +02:00
void DiffViewEditorWidget : : setDisplaySettings ( const DisplaySettings & ds )
{
DisplaySettings settings = displaySettings ( ) ;
settings . m_visualizeWhitespace = ds . m_visualizeWhitespace ;
2013-08-14 13:52:13 +02:00
BaseTextEditorWidget : : setDisplaySettings ( settings ) ;
2013-06-03 15:17:34 +02:00
}
2014-01-21 11:26:39 +01:00
void DiffViewEditorWidget : : applyFontSettings ( )
2013-06-05 15:25:42 +02:00
{
2014-01-21 11:26:39 +01:00
BaseTextEditorWidget : : applyFontSettings ( ) ;
const TextEditor : : FontSettings & fs = baseTextDocument ( ) - > fontSettings ( ) ;
2013-06-05 15:25:42 +02:00
m_fileLineForeground = fs . formatFor ( C_DIFF_FILE_LINE ) . foreground ( ) ;
m_chunkLineForeground = fs . formatFor ( C_DIFF_CONTEXT_LINE ) . foreground ( ) ;
m_textForeground = fs . toTextCharFormat ( C_TEXT ) . foreground ( ) . color ( ) ;
update ( ) ;
}
2013-02-15 12:49:50 +01:00
QString DiffViewEditorWidget : : lineNumber ( int blockNumber ) const
{
2013-04-26 11:50:10 +02:00
if ( m_lineNumbers . contains ( blockNumber ) )
return QString : : number ( m_lineNumbers . value ( blockNumber ) ) ;
return QString ( ) ;
2013-02-15 12:49:50 +01:00
}
2013-04-09 10:26:31 +02:00
int DiffViewEditorWidget : : lineNumberDigits ( ) const
2013-02-15 12:49:50 +01:00
{
2013-04-09 10:26:31 +02:00
return m_lineNumberDigits ;
2013-02-15 12:49:50 +01:00
}
2013-04-09 10:26:31 +02:00
bool DiffViewEditorWidget : : selectionVisible ( int blockNumber ) const
2013-02-15 12:49:50 +01:00
{
2013-04-09 10:26:31 +02:00
return ! m_separators . value ( blockNumber , false ) ;
2013-02-15 12:49:50 +01:00
}
2013-04-10 13:51:04 +02:00
bool DiffViewEditorWidget : : replacementVisible ( int blockNumber ) const
{
2013-04-25 17:37:20 +02:00
return isChunkLine ( blockNumber ) | | ( isFileLine ( blockNumber )
& & TextEditor : : BaseTextDocumentLayout : : isFolded ( document ( ) - > findBlockByNumber ( blockNumber ) ) ) ;
2013-04-10 13:51:04 +02:00
}
2013-06-05 15:25:42 +02:00
QColor DiffViewEditorWidget : : replacementPenColor ( int blockNumber ) const
{
Q_UNUSED ( blockNumber )
return m_chunkLineForeground ;
}
2013-04-23 09:09:20 +02:00
QString DiffViewEditorWidget : : plainTextFromSelection ( const QTextCursor & cursor ) const
{
const int startPosition = cursor . selectionStart ( ) ;
const int endPosition = cursor . selectionEnd ( ) ;
if ( startPosition = = endPosition )
return QString ( ) ; // no selection
QTextBlock startBlock = document ( ) - > findBlock ( startPosition ) ;
QTextBlock endBlock = document ( ) - > findBlock ( endPosition ) ;
QTextBlock block = startBlock ;
QString text ;
bool textInserted = false ;
while ( block . isValid ( ) & & block . blockNumber ( ) < = endBlock . blockNumber ( ) ) {
if ( selectionVisible ( block . blockNumber ( ) ) ) {
if ( block = = startBlock ) {
if ( block = = endBlock )
text = cursor . selectedText ( ) ; // just one line text
else
text = block . text ( ) . mid ( startPosition - block . position ( ) ) ;
} else {
if ( textInserted )
text + = QLatin1Char ( ' \n ' ) ;
if ( block = = endBlock )
text + = block . text ( ) . left ( endPosition - block . position ( ) ) ;
else
text + = block . text ( ) ;
}
textInserted = true ;
}
block = block . next ( ) ;
}
return convertToPlainText ( text ) ;
}
2013-04-26 11:50:10 +02:00
void DiffViewEditorWidget : : setLineNumber ( int blockNumber , int lineNumber )
2013-02-15 12:49:50 +01:00
{
2013-04-26 11:50:10 +02:00
const QString lineNumberString = QString : : number ( lineNumber ) ;
2013-02-15 12:49:50 +01:00
m_lineNumbers . insert ( blockNumber , lineNumber ) ;
2013-04-26 11:50:10 +02:00
m_lineNumberDigits = qMax ( m_lineNumberDigits , lineNumberString . count ( ) ) ;
2013-02-15 12:49:50 +01:00
}
2013-12-16 16:19:40 +01:00
void DiffViewEditorWidget : : setFileInfo ( int blockNumber , const DiffEditorController : : DiffFileInfo & fileInfo )
2013-08-14 13:52:13 +02:00
{
m_fileInfo [ blockNumber ] = fileInfo ;
setSeparator ( blockNumber , true ) ;
}
2013-05-23 13:36:27 +02:00
int DiffViewEditorWidget : : blockNumberForFileIndex ( int fileIndex ) const
{
if ( fileIndex < 0 | | fileIndex > = m_fileInfo . count ( ) )
return - 1 ;
2013-12-16 16:19:40 +01:00
QMap < int , DiffEditorController : : DiffFileInfo > : : const_iterator it
2013-05-23 13:36:27 +02:00
= m_fileInfo . constBegin ( ) ;
for ( int i = 0 ; i < fileIndex ; i + + )
+ + it ;
return it . key ( ) ;
}
int DiffViewEditorWidget : : fileIndexForBlockNumber ( int blockNumber ) const
{
2013-12-16 16:19:40 +01:00
QMap < int , DiffEditorController : : DiffFileInfo > : : const_iterator it
2013-05-23 13:36:27 +02:00
= m_fileInfo . constBegin ( ) ;
2013-12-16 16:19:40 +01:00
QMap < int , DiffEditorController : : DiffFileInfo > : : const_iterator itEnd
2013-05-23 13:36:27 +02:00
= m_fileInfo . constEnd ( ) ;
int i = - 1 ;
while ( it ! = itEnd ) {
if ( it . key ( ) > blockNumber )
break ;
+ + it ;
+ + i ;
}
return i ;
}
2013-05-07 14:02:08 +02:00
void DiffViewEditorWidget : : clearAll ( const QString & message )
{
setBlockSelection ( false ) ;
clear ( ) ;
clearAllData ( ) ;
setPlainText ( message ) ;
2013-12-16 16:19:40 +01:00
m_highlighter - > setDocuments ( QList < QPair < DiffEditorController : : DiffFileInfo , QString > > ( ) ) ;
2013-05-07 14:02:08 +02:00
}
2013-04-25 17:37:20 +02:00
void DiffViewEditorWidget : : clearAllData ( )
2013-02-15 12:49:50 +01:00
{
m_lineNumberDigits = 1 ;
2013-04-25 17:37:20 +02:00
m_lineNumbers . clear ( ) ;
2013-05-07 14:02:08 +02:00
m_fileInfo . clear ( ) ;
2013-04-25 17:37:20 +02:00
m_skippedLines . clear ( ) ;
m_separators . clear ( ) ;
2013-02-15 12:49:50 +01:00
}
2013-12-16 16:19:40 +01:00
void DiffViewEditorWidget : : setDocuments ( const QList < QPair < DiffEditorController : : DiffFileInfo , QString > > & documents )
2013-08-14 13:52:13 +02:00
{
m_highlighter - > setDocuments ( documents ) ;
}
2013-02-15 12:49:50 +01:00
void DiffViewEditorWidget : : scrollContentsBy ( int dx , int dy )
{
2013-08-14 13:52:13 +02:00
BaseTextEditorWidget : : scrollContentsBy ( dx , dy ) ;
2013-02-15 12:49:50 +01:00
// TODO: update only chunk lines
viewport ( ) - > update ( ) ;
}
2013-06-05 15:25:42 +02:00
void DiffViewEditorWidget : : paintSeparator ( QPainter & painter ,
QColor & color ,
const QString & text ,
const QTextBlock & block ,
int top )
2013-04-25 17:37:20 +02:00
{
QPointF offset = contentOffset ( ) ;
painter . save ( ) ;
2013-06-05 15:25:42 +02:00
QColor foreground = color ;
if ( ! foreground . isValid ( ) )
foreground = m_textForeground ;
if ( ! foreground . isValid ( ) )
foreground = palette ( ) . foreground ( ) . color ( ) ;
painter . setPen ( foreground ) ;
2013-04-25 17:37:20 +02:00
const QString replacementText = QLatin1String ( " { " )
+ foldReplacementText ( block )
+ QLatin1String ( " }; " ) ;
const int replacementTextWidth = fontMetrics ( ) . width ( replacementText ) + 24 ;
int x = replacementTextWidth + offset . x ( ) ;
if ( x < document ( ) - > documentMargin ( ) | | ! TextEditor : : BaseTextDocumentLayout : : isFolded ( block ) )
x = document ( ) - > documentMargin ( ) ;
const QString elidedText = fontMetrics ( ) . elidedText ( text ,
Qt : : ElideRight ,
viewport ( ) - > width ( ) - x ) ;
QTextLayout * layout = block . layout ( ) ;
QTextLine textLine = layout - > lineAt ( 0 ) ;
QRectF lineRect = textLine . naturalTextRect ( ) . translated ( offset . x ( ) , top ) ;
QRect clipRect = contentsRect ( ) ;
clipRect . setLeft ( x ) ;
painter . setClipRect ( clipRect ) ;
painter . drawText ( QPointF ( x , lineRect . top ( ) + textLine . ascent ( ) ) ,
elidedText ) ;
painter . restore ( ) ;
}
2013-04-26 11:50:10 +02:00
void DiffViewEditorWidget : : mouseDoubleClickEvent ( QMouseEvent * e )
{
if ( e - > button ( ) = = Qt : : LeftButton & & ! ( e - > modifiers ( ) & Qt : : ShiftModifier ) ) {
QTextCursor cursor = cursorForPosition ( e - > pos ( ) ) ;
jumpToOriginalFile ( cursor ) ;
2013-07-01 11:38:16 +02:00
e - > accept ( ) ;
return ;
2013-04-26 11:50:10 +02:00
}
2013-08-14 13:52:13 +02:00
BaseTextEditorWidget : : mouseDoubleClickEvent ( e ) ;
2013-04-26 11:50:10 +02:00
}
void DiffViewEditorWidget : : jumpToOriginalFile ( const QTextCursor & cursor )
{
2013-05-07 14:02:08 +02:00
if ( m_fileInfo . isEmpty ( ) )
2013-04-26 11:50:10 +02:00
return ;
const int blockNumber = cursor . blockNumber ( ) ;
2013-07-01 13:15:27 +02:00
const int columnNumber = cursor . positionInBlock ( ) ;
2013-04-26 11:50:10 +02:00
if ( ! m_lineNumbers . contains ( blockNumber ) )
return ;
2013-07-01 13:15:27 +02:00
const int lineNumber = m_lineNumbers . value ( blockNumber ) ;
2013-04-26 11:50:10 +02:00
2013-07-01 13:15:27 +02:00
emit jumpToOriginalFileRequested ( fileIndexForBlockNumber ( blockNumber ) , lineNumber , columnNumber ) ;
2013-04-26 11:50:10 +02:00
}
2013-02-15 12:49:50 +01:00
void DiffViewEditorWidget : : paintEvent ( QPaintEvent * e )
{
2013-04-25 17:37:20 +02:00
m_inPaintEvent = true ;
2013-08-14 13:52:13 +02:00
BaseTextEditorWidget : : paintEvent ( e ) ;
2013-04-25 17:37:20 +02:00
m_inPaintEvent = false ;
2013-02-15 12:49:50 +01:00
QPainter painter ( viewport ( ) ) ;
QPointF offset = contentOffset ( ) ;
QTextBlock firstBlock = firstVisibleBlock ( ) ;
2013-02-21 17:14:07 +01:00
QTextBlock currentBlock = firstBlock ;
while ( currentBlock . isValid ( ) ) {
if ( currentBlock . isVisible ( ) ) {
qreal top = blockBoundingGeometry ( currentBlock ) . translated ( offset ) . top ( ) ;
qreal bottom = top + blockBoundingRect ( currentBlock ) . height ( ) ;
2013-02-15 12:49:50 +01:00
2013-02-21 17:14:07 +01:00
if ( top > e - > rect ( ) . bottom ( ) )
break ;
if ( bottom > = e - > rect ( ) . top ( ) ) {
2013-04-25 17:37:20 +02:00
const int blockNumber = currentBlock . blockNumber ( ) ;
2013-02-21 17:14:07 +01:00
2013-04-25 17:37:20 +02:00
const int skippedBefore = m_skippedLines . value ( blockNumber ) ;
2013-04-09 10:26:31 +02:00
if ( skippedBefore ) {
2013-04-25 17:37:20 +02:00
const QString skippedRowsText = tr ( " Skipped %n lines... " , 0 , skippedBefore ) ;
2013-06-05 15:25:42 +02:00
paintSeparator ( painter , m_chunkLineForeground ,
skippedRowsText , currentBlock , top ) ;
2013-04-25 17:37:20 +02:00
}
2013-12-16 16:19:40 +01:00
const DiffEditorController : : DiffFileInfo fileInfo = m_fileInfo . value ( blockNumber ) ;
2013-05-07 14:02:08 +02:00
if ( ! fileInfo . fileName . isEmpty ( ) ) {
const QString fileNameText = fileInfo . typeInfo . isEmpty ( )
? fileInfo . fileName
: tr ( " [%1] %2 " ) . arg ( fileInfo . typeInfo ) . arg ( fileInfo . fileName ) ;
2013-06-05 15:25:42 +02:00
paintSeparator ( painter , m_fileLineForeground ,
fileNameText , currentBlock , top ) ;
2013-02-21 17:14:07 +01:00
}
2013-02-15 12:49:50 +01:00
}
}
2013-02-21 17:14:07 +01:00
currentBlock = currentBlock . next ( ) ;
2013-02-15 12:49:50 +01:00
}
2013-04-25 17:37:20 +02:00
paintCollapsedBlockPopup ( painter , e - > rect ( ) ) ;
2013-02-15 12:49:50 +01:00
}
2013-04-25 17:37:20 +02:00
void DiffViewEditorWidget : : paintCollapsedBlockPopup ( QPainter & painter , const QRect & clipRect )
{
QPointF offset ( contentOffset ( ) ) ;
QRect viewportRect = viewport ( ) - > rect ( ) ;
QTextBlock block = firstVisibleBlock ( ) ;
QTextBlock visibleCollapsedBlock ;
QPointF visibleCollapsedBlockOffset ;
while ( block . isValid ( ) ) {
QRectF r = blockBoundingRect ( block ) . translated ( offset ) ;
offset . ry ( ) + = r . height ( ) ;
if ( offset . y ( ) > viewportRect . height ( ) )
break ;
block = block . next ( ) ;
if ( ! block . isVisible ( ) ) {
if ( block . blockNumber ( ) = = visibleFoldedBlockNumber ( ) ) {
visibleCollapsedBlock = block ;
visibleCollapsedBlockOffset = offset + QPointF ( 0 , 1 ) ;
break ;
}
// invisible blocks do have zero line count
block = document ( ) - > findBlockByLineNumber ( block . firstLineNumber ( ) ) ;
}
}
if ( visibleCollapsedBlock . isValid ( ) ) {
drawCollapsedBlockPopup ( painter ,
visibleCollapsedBlock ,
visibleCollapsedBlockOffset ,
clipRect ) ;
}
}
void DiffViewEditorWidget : : drawCollapsedBlockPopup ( QPainter & painter ,
const QTextBlock & block ,
QPointF offset ,
const QRect & clip )
{
// We ignore the call coming from the BaseTextEditorWidget::paintEvent()
// since we will draw it later, after custom drawings of this paintEvent.
// We need to draw it after our custom drawings, otherwise custom
// drawings will appear in front of block popup.
if ( m_inPaintEvent )
return ;
int margin = block . document ( ) - > documentMargin ( ) ;
qreal maxWidth = 0 ;
qreal blockHeight = 0 ;
QTextBlock b = block ;
while ( ! b . isVisible ( ) ) {
if ( ! m_separators . contains ( b . blockNumber ( ) ) ) {
b . setVisible ( true ) ; // make sure block bounding rect works
QRectF r = blockBoundingRect ( b ) . translated ( offset ) ;
QTextLayout * layout = b . layout ( ) ;
for ( int i = layout - > lineCount ( ) - 1 ; i > = 0 ; - - i )
maxWidth = qMax ( maxWidth , layout - > lineAt ( i ) . naturalTextWidth ( ) + 2 * margin ) ;
blockHeight + = r . height ( ) ;
b . setVisible ( false ) ; // restore previous state
b . setLineCount ( 0 ) ; // restore 0 line count for invisible block
}
b = b . next ( ) ;
}
painter . save ( ) ;
painter . setRenderHint ( QPainter : : Antialiasing , true ) ;
painter . translate ( .5 , .5 ) ;
QBrush brush = palette ( ) . base ( ) ;
painter . setBrush ( brush ) ;
painter . drawRoundedRect ( QRectF ( offset . x ( ) ,
offset . y ( ) ,
maxWidth , blockHeight ) . adjusted ( 0 , 0 , 0 , 0 ) , 3 , 3 ) ;
painter . restore ( ) ;
QTextBlock end = b ;
b = block ;
while ( b ! = end ) {
if ( ! m_separators . contains ( b . blockNumber ( ) ) ) {
b . setVisible ( true ) ; // make sure block bounding rect works
QRectF r = blockBoundingRect ( b ) . translated ( offset ) ;
QTextLayout * layout = b . layout ( ) ;
QVector < QTextLayout : : FormatRange > selections ;
layout - > draw ( & painter , offset , selections , clip ) ;
b . setVisible ( false ) ; // restore previous state
b . setLineCount ( 0 ) ; // restore 0 line count for invisible block
offset . ry ( ) + = r . height ( ) ;
}
b = b . next ( ) ;
}
}
2013-02-15 12:49:50 +01:00
//////////////////
DiffEditorWidget : : DiffEditorWidget ( QWidget * parent )
2013-12-16 16:19:40 +01:00
: QWidget ( parent )
, m_controller ( 0 )
, m_foldingBlocker ( false )
2013-02-15 12:49:50 +01:00
{
m_leftEditor = new DiffViewEditorWidget ( this ) ;
m_leftEditor - > setVerticalScrollBarPolicy ( Qt : : ScrollBarAlwaysOff ) ;
m_leftEditor - > setReadOnly ( true ) ;
2013-09-19 17:59:27 +02:00
connect ( TextEditorSettings : : instance ( ) ,
SIGNAL ( displaySettingsChanged ( TextEditor : : DisplaySettings ) ) ,
2013-06-03 15:17:34 +02:00
m_leftEditor , SLOT ( setDisplaySettings ( TextEditor : : DisplaySettings ) ) ) ;
2013-09-19 17:59:27 +02:00
m_leftEditor - > setDisplaySettings ( TextEditorSettings : : displaySettings ( ) ) ;
m_leftEditor - > setCodeStyle ( TextEditorSettings : : codeStyle ( ) ) ;
2013-07-01 13:15:27 +02:00
connect ( m_leftEditor , SIGNAL ( jumpToOriginalFileRequested ( int , int , int ) ) ,
this , SLOT ( slotLeftJumpToOriginalFileRequested ( int , int , int ) ) ) ;
2013-02-15 12:49:50 +01:00
m_rightEditor = new DiffViewEditorWidget ( this ) ;
m_rightEditor - > setReadOnly ( true ) ;
2013-09-19 17:59:27 +02:00
connect ( TextEditorSettings : : instance ( ) ,
SIGNAL ( displaySettingsChanged ( TextEditor : : DisplaySettings ) ) ,
2013-06-03 15:17:34 +02:00
m_rightEditor , SLOT ( setDisplaySettings ( TextEditor : : DisplaySettings ) ) ) ;
2013-09-19 17:59:27 +02:00
m_rightEditor - > setDisplaySettings ( TextEditorSettings : : displaySettings ( ) ) ;
m_rightEditor - > setCodeStyle ( TextEditorSettings : : codeStyle ( ) ) ;
2013-07-01 13:15:27 +02:00
connect ( m_rightEditor , SIGNAL ( jumpToOriginalFileRequested ( int , int , int ) ) ,
this , SLOT ( slotRightJumpToOriginalFileRequested ( int , int , int ) ) ) ;
2013-02-15 12:49:50 +01:00
2013-09-19 17:59:27 +02:00
connect ( TextEditorSettings : : instance ( ) ,
SIGNAL ( fontSettingsChanged ( TextEditor : : FontSettings ) ) ,
2013-06-05 15:25:42 +02:00
this , SLOT ( setFontSettings ( TextEditor : : FontSettings ) ) ) ;
2013-09-19 17:59:27 +02:00
setFontSettings ( TextEditorSettings : : fontSettings ( ) ) ;
2013-06-05 15:25:42 +02:00
2013-02-15 12:49:50 +01:00
connect ( m_leftEditor - > verticalScrollBar ( ) , SIGNAL ( valueChanged ( int ) ) ,
2013-05-22 16:33:44 +02:00
this , SLOT ( leftVSliderChanged ( ) ) ) ;
2013-02-15 12:49:50 +01:00
connect ( m_leftEditor - > verticalScrollBar ( ) , SIGNAL ( actionTriggered ( int ) ) ,
2013-05-22 16:33:44 +02:00
this , SLOT ( leftVSliderChanged ( ) ) ) ;
connect ( m_leftEditor - > horizontalScrollBar ( ) , SIGNAL ( valueChanged ( int ) ) ,
this , SLOT ( leftHSliderChanged ( ) ) ) ;
connect ( m_leftEditor - > horizontalScrollBar ( ) , SIGNAL ( actionTriggered ( int ) ) ,
this , SLOT ( leftHSliderChanged ( ) ) ) ;
2013-05-23 13:36:27 +02:00
connect ( m_leftEditor , SIGNAL ( cursorPositionChanged ( ) ) ,
this , SLOT ( leftCursorPositionChanged ( ) ) ) ;
2013-04-10 13:51:04 +02:00
connect ( m_leftEditor - > document ( ) - > documentLayout ( ) , SIGNAL ( documentSizeChanged ( QSizeF ) ) ,
this , SLOT ( leftDocumentSizeChanged ( ) ) ) ;
2013-05-22 16:33:44 +02:00
2013-02-15 12:49:50 +01:00
connect ( m_rightEditor - > verticalScrollBar ( ) , SIGNAL ( valueChanged ( int ) ) ,
2013-05-22 16:33:44 +02:00
this , SLOT ( rightVSliderChanged ( ) ) ) ;
2013-02-15 12:49:50 +01:00
connect ( m_rightEditor - > verticalScrollBar ( ) , SIGNAL ( actionTriggered ( int ) ) ,
2013-05-22 16:33:44 +02:00
this , SLOT ( rightVSliderChanged ( ) ) ) ;
connect ( m_rightEditor - > horizontalScrollBar ( ) , SIGNAL ( valueChanged ( int ) ) ,
this , SLOT ( rightHSliderChanged ( ) ) ) ;
connect ( m_rightEditor - > horizontalScrollBar ( ) , SIGNAL ( actionTriggered ( int ) ) ,
this , SLOT ( rightHSliderChanged ( ) ) ) ;
2013-05-23 13:36:27 +02:00
connect ( m_rightEditor , SIGNAL ( cursorPositionChanged ( ) ) ,
this , SLOT ( rightCursorPositionChanged ( ) ) ) ;
2013-04-10 13:51:04 +02:00
connect ( m_rightEditor - > document ( ) - > documentLayout ( ) , SIGNAL ( documentSizeChanged ( QSizeF ) ) ,
this , SLOT ( rightDocumentSizeChanged ( ) ) ) ;
2013-02-15 12:49:50 +01:00
2013-05-24 13:22:46 +02:00
m_splitter = new Core : : MiniSplitter ( this ) ;
2013-02-15 12:49:50 +01:00
m_splitter - > addWidget ( m_leftEditor ) ;
m_splitter - > addWidget ( m_rightEditor ) ;
QVBoxLayout * l = new QVBoxLayout ( this ) ;
2013-05-22 16:33:44 +02:00
l - > setMargin ( 0 ) ;
2013-02-15 12:49:50 +01:00
l - > addWidget ( m_splitter ) ;
2013-05-07 14:02:08 +02:00
2013-12-16 16:19:40 +01:00
clear ( tr ( " No controller " ) ) ;
2013-02-15 12:49:50 +01:00
}
DiffEditorWidget : : ~ DiffEditorWidget ( )
{
}
2013-12-16 16:19:40 +01:00
void DiffEditorWidget : : setDiffEditorController ( DiffEditorController * controller )
2013-05-07 14:02:08 +02:00
{
2013-12-16 16:19:40 +01:00
if ( m_controller ) {
disconnect ( m_controller , SIGNAL ( cleared ( QString ) ) , this , SLOT ( clear ( QString ) ) ) ;
disconnect ( m_controller , SIGNAL ( diffContentsChanged ( QList < DiffEditorController : : DiffFilesContents > , QString ) ) ,
this , SLOT ( setDiff ( QList < DiffEditorController : : DiffFilesContents > , QString ) ) ) ;
disconnect ( m_controller , SIGNAL ( contextLinesNumberChanged ( int ) ) ,
this , SLOT ( setContextLinesNumber ( int ) ) ) ;
disconnect ( m_controller , SIGNAL ( ignoreWhitespacesChanged ( bool ) ) ,
this , SLOT ( setIgnoreWhitespaces ( bool ) ) ) ;
disconnect ( m_controller , SIGNAL ( currentDiffFileIndexChanged ( int ) ) ,
this , SLOT ( setCurrentDiffFileIndex ( int ) ) ) ;
clear ( tr ( " No controller " ) ) ;
}
m_controller = controller ;
if ( m_controller ) {
connect ( m_controller , SIGNAL ( cleared ( QString ) ) , this , SLOT ( clear ( QString ) ) ) ;
connect ( m_controller , SIGNAL ( diffContentsChanged ( QList < DiffEditorController : : DiffFilesContents > , QString ) ) ,
this , SLOT ( setDiff ( QList < DiffEditorController : : DiffFilesContents > , QString ) ) ) ;
connect ( m_controller , SIGNAL ( contextLinesNumberChanged ( int ) ) ,
this , SLOT ( setContextLinesNumber ( int ) ) ) ;
connect ( m_controller , SIGNAL ( ignoreWhitespacesChanged ( bool ) ) ,
this , SLOT ( setIgnoreWhitespaces ( bool ) ) ) ;
connect ( m_controller , SIGNAL ( currentDiffFileIndexChanged ( int ) ) ,
this , SLOT ( setCurrentDiffFileIndex ( int ) ) ) ;
setDiff ( m_controller - > diffContents ( ) , m_controller - > workingDirectory ( ) ) ;
}
}
DiffEditorController * DiffEditorWidget : : diffEditorController ( ) const
{
return m_controller ;
2013-05-07 14:02:08 +02:00
}
void DiffEditorWidget : : clear ( const QString & message )
{
m_leftEditor - > clearAll ( message ) ;
m_rightEditor - > clearAll ( message ) ;
}
2013-12-16 16:19:40 +01:00
void DiffEditorWidget : : setDiff ( const QList < DiffEditorController : : DiffFilesContents > & diffFileList , const QString & workingDirectory )
2013-02-15 12:49:50 +01:00
{
2013-12-16 16:19:40 +01:00
Q_UNUSED ( workingDirectory )
2013-03-13 13:42:26 +01:00
Differ differ ;
2013-04-25 17:37:20 +02:00
QList < DiffList > diffList ;
for ( int i = 0 ; i < diffFileList . count ( ) ; i + + ) {
2013-12-16 16:19:40 +01:00
DiffEditorController : : DiffFilesContents dfc = diffFileList . at ( i ) ;
2013-04-25 17:37:20 +02:00
DiffList dl ;
2013-05-07 14:02:08 +02:00
dl . leftFileInfo = dfc . leftFileInfo ;
dl . rightFileInfo = dfc . rightFileInfo ;
2013-04-25 17:37:20 +02:00
dl . diffList = differ . cleanupSemantics ( differ . diff ( dfc . leftText , dfc . rightText ) ) ;
diffList . append ( dl ) ;
}
setDiff ( diffList ) ;
2013-02-15 12:49:50 +01:00
}
2013-04-25 17:37:20 +02:00
void DiffEditorWidget : : setDiff ( const QList < DiffList > & diffList )
2013-02-15 12:49:50 +01:00
{
m_diffList = diffList ;
2013-04-25 17:37:20 +02:00
m_originalChunkData . clear ( ) ;
m_contextFileData . clear ( ) ;
for ( int i = 0 ; i < m_diffList . count ( ) ; i + + ) {
const DiffList & dl = m_diffList . at ( i ) ;
ChunkData chunkData = calculateOriginalData ( dl . diffList ) ;
m_originalChunkData . append ( chunkData ) ;
FileData fileData = calculateContextData ( chunkData ) ;
2013-05-07 14:02:08 +02:00
fileData . leftFileInfo = dl . leftFileInfo ;
fileData . rightFileInfo = dl . rightFileInfo ;
2013-04-25 17:37:20 +02:00
m_contextFileData . append ( fileData ) ;
}
2013-02-15 12:49:50 +01:00
showDiff ( ) ;
}
void DiffEditorWidget : : setContextLinesNumber ( int lines )
{
2013-12-16 16:19:40 +01:00
Q_UNUSED ( lines )
2013-02-15 12:49:50 +01:00
2013-04-25 17:37:20 +02:00
for ( int i = 0 ; i < m_diffList . count ( ) ; i + + ) {
const FileData oldFileData = m_contextFileData . at ( i ) ;
FileData newFileData = calculateContextData ( m_originalChunkData . at ( i ) ) ;
2013-05-07 14:02:08 +02:00
newFileData . leftFileInfo = oldFileData . leftFileInfo ;
newFileData . rightFileInfo = oldFileData . rightFileInfo ;
2013-04-25 17:37:20 +02:00
m_contextFileData [ i ] = newFileData ;
}
2013-02-15 12:49:50 +01:00
showDiff ( ) ;
}
void DiffEditorWidget : : setIgnoreWhitespaces ( bool ignore )
{
2013-12-16 16:19:40 +01:00
Q_UNUSED ( ignore )
2013-02-15 12:49:50 +01:00
setDiff ( m_diffList ) ;
}
2013-12-16 16:19:40 +01:00
void DiffEditorWidget : : setCurrentDiffFileIndex ( int diffFileIndex )
2013-05-23 13:36:27 +02:00
{
const int blockNumber = m_leftEditor - > blockNumberForFileIndex ( diffFileIndex ) ;
QTextBlock leftBlock = m_leftEditor - > document ( ) - > findBlockByNumber ( blockNumber ) ;
QTextCursor leftCursor = m_leftEditor - > textCursor ( ) ;
leftCursor . setPosition ( leftBlock . position ( ) ) ;
m_leftEditor - > setTextCursor ( leftCursor ) ;
QTextBlock rightBlock = m_rightEditor - > document ( ) - > findBlockByNumber ( blockNumber ) ;
QTextCursor rightCursor = m_rightEditor - > textCursor ( ) ;
rightCursor . setPosition ( rightBlock . position ( ) ) ;
m_rightEditor - > setTextCursor ( rightCursor ) ;
m_leftEditor - > centerCursor ( ) ;
m_rightEditor - > centerCursor ( ) ;
}
2013-02-15 12:49:50 +01:00
QTextCodec * DiffEditorWidget : : codec ( ) const
{
return const_cast < QTextCodec * > ( m_leftEditor - > codec ( ) ) ;
}
bool DiffEditorWidget : : isWhitespace ( const QChar & c ) const
{
if ( c = = QLatin1Char ( ' ' ) | | c = = QLatin1Char ( ' \t ' ) )
return true ;
return false ;
}
bool DiffEditorWidget : : isWhitespace ( const Diff & diff ) const
{
for ( int i = 0 ; i < diff . text . count ( ) ; i + + ) {
if ( ! isWhitespace ( diff . text . at ( i ) ) )
return false ;
}
return true ;
}
bool DiffEditorWidget : : isEqual ( const QList < Diff > & diffList , int diffNumber ) const
{
2014-01-15 09:50:23 +01:00
if ( diffNumber = = diffList . count ( ) )
return true ;
2013-02-15 12:49:50 +01:00
const Diff & diff = diffList . at ( diffNumber ) ;
if ( diff . command = = Diff : : Equal )
return true ;
if ( diff . text . count ( ) = = 0 )
return true ;
2013-12-16 16:19:40 +01:00
if ( m_controller & & ! m_controller - > isIgnoreWhitespaces ( ) )
2013-02-15 12:49:50 +01:00
return false ;
if ( isWhitespace ( diff ) = = false )
return false ;
if ( diffNumber = = 0 | | diffNumber = = diffList . count ( ) - 1 )
return false ; // it's a Diff start or end
// Examine previous diff
if ( diffNumber > 0 ) {
const Diff & previousDiff = diffList . at ( diffNumber - 1 ) ;
if ( previousDiff . command = = Diff : : Equal ) {
const int previousDiffCount = previousDiff . text . count ( ) ;
if ( previousDiffCount & & isWhitespace ( previousDiff . text . at ( previousDiffCount - 1 ) ) )
return true ;
} else if ( diff . command ! = previousDiff . command
& & isWhitespace ( previousDiff ) ) {
return true ;
}
}
// Examine next diff
if ( diffNumber < diffList . count ( ) - 1 ) {
const Diff & nextDiff = diffList . at ( diffNumber + 1 ) ;
if ( nextDiff . command = = Diff : : Equal ) {
const int nextDiffCount = nextDiff . text . count ( ) ;
if ( nextDiffCount & & isWhitespace ( nextDiff . text . at ( 0 ) ) )
return true ;
} else if ( diff . command ! = nextDiff . command
& & isWhitespace ( nextDiff ) ) {
return true ;
}
}
return false ;
}
2013-04-23 14:35:49 +02:00
QList < TextLineData > DiffEditorWidget : : assemblyRows ( const QStringList & lines ,
const QMap < int , int > & lineSpans ,
const QMap < int , int > & changedPositions ,
QMap < int , int > * outputChangedPositions ) const
{
QList < TextLineData > data ;
2013-06-07 15:23:12 +02:00
int previousSpanOffset = 0 ;
2013-04-23 14:35:49 +02:00
int spanOffset = 0 ;
int pos = 0 ;
2013-06-07 15:23:12 +02:00
bool usePreviousSpanOffsetForStartPosition = false ;
2013-04-23 14:35:49 +02:00
QMap < int , int > : : ConstIterator changedIt = changedPositions . constBegin ( ) ;
QMap < int , int > : : ConstIterator changedEnd = changedPositions . constEnd ( ) ;
const int lineCount = lines . count ( ) ;
for ( int i = 0 ; i < = lineCount ; i + + ) {
for ( int j = 0 ; j < lineSpans . value ( i ) ; j + + ) {
data . append ( TextLineData ( TextLineData : : Separator ) ) ;
spanOffset + + ;
}
if ( i < lineCount ) {
const int textLength = lines . at ( i ) . count ( ) + 1 ;
pos + = textLength ;
data . append ( lines . at ( i ) ) ;
}
while ( changedIt ! = changedEnd ) {
if ( changedIt . key ( ) > = pos )
break ;
2013-06-07 15:23:12 +02:00
if ( changedIt . value ( ) > = pos ) {
usePreviousSpanOffsetForStartPosition = true ;
previousSpanOffset = spanOffset ;
break ;
}
const int startSpanOffset = usePreviousSpanOffsetForStartPosition
? previousSpanOffset : spanOffset ;
usePreviousSpanOffsetForStartPosition = false ;
const int startPos = changedIt . key ( ) + startSpanOffset ;
2013-04-23 14:35:49 +02:00
const int endPos = changedIt . value ( ) + spanOffset ;
if ( outputChangedPositions )
outputChangedPositions - > insert ( startPos , endPos ) ;
+ + changedIt ;
}
}
return data ;
}
2013-02-15 12:49:50 +01:00
ChunkData DiffEditorWidget : : calculateOriginalData ( const QList < Diff > & diffList ) const
{
ChunkData chunkData ;
QStringList leftLines ;
QStringList rightLines ;
leftLines . append ( QString ( ) ) ;
rightLines . append ( QString ( ) ) ;
QMap < int , int > leftLineSpans ;
QMap < int , int > rightLineSpans ;
QMap < int , int > leftChangedPositions ;
QMap < int , int > rightChangedPositions ;
QList < int > leftEqualLines ;
QList < int > rightEqualLines ;
int currentLeftLine = 0 ;
int currentLeftPos = 0 ;
int currentLeftLineOffset = 0 ;
int currentRightLine = 0 ;
int currentRightPos = 0 ;
int currentRightLineOffset = 0 ;
int lastAlignedLeftLine = - 1 ;
int lastAlignedRightLine = - 1 ;
bool lastLeftLineEqual = true ;
bool lastRightLineEqual = true ;
2014-01-15 09:50:23 +01:00
for ( int i = 0 ; i < = diffList . count ( ) ; i + + ) {
Diff diff = i < diffList . count ( )
? diffList . at ( i )
: Diff ( Diff : : Equal , QLatin1String ( " " ) ) ; // dummy, ensure we process to the end even when diffList doesn't end with equality
2013-02-15 12:49:50 +01:00
const QStringList lines = diff . text . split ( QLatin1Char ( ' \n ' ) ) ;
const bool equal = isEqual ( diffList , i ) ;
2013-11-11 22:20:47 +02:00
if ( diff . command = = Diff : : Insert )
2013-04-23 14:35:49 +02:00
lastRightLineEqual = lastRightLineEqual & & equal ;
2013-11-11 22:20:47 +02:00
else if ( diff . command = = Diff : : Delete )
2013-04-23 14:35:49 +02:00
lastLeftLineEqual = lastLeftLineEqual & & equal ;
2013-02-15 12:49:50 +01:00
const int lastLeftPos = currentLeftPos ;
const int lastRightPos = currentRightPos ;
for ( int j = 0 ; j < lines . count ( ) ; j + + ) {
const QString line = lines . at ( j ) ;
2014-01-15 09:50:23 +01:00
if ( j > 0 | | i = = diffList . count ( ) ) {
if ( diff . command = = Diff : : Equal & & lastLeftLineEqual & & lastRightLineEqual ) {
leftEqualLines . append ( currentLeftLine ) ;
rightEqualLines . append ( currentRightLine ) ;
2013-02-15 12:49:50 +01:00
}
2014-01-15 09:50:23 +01:00
}
2013-02-15 12:49:50 +01:00
2014-01-15 09:50:23 +01:00
if ( j > 0 ) {
2013-02-15 12:49:50 +01:00
if ( diff . command ! = Diff : : Insert ) {
currentLeftLine + + ;
currentLeftLineOffset + + ;
leftLines . append ( QString ( ) ) ;
currentLeftPos + + ;
2013-04-23 14:35:49 +02:00
lastLeftLineEqual = ! line . count ( ) | | equal ;
2013-02-15 12:49:50 +01:00
}
if ( diff . command ! = Diff : : Delete ) {
currentRightLine + + ;
currentRightLineOffset + + ;
rightLines . append ( QString ( ) ) ;
currentRightPos + + ;
2013-04-23 14:35:49 +02:00
lastRightLineEqual = ! line . count ( ) | | equal ;
2013-02-15 12:49:50 +01:00
}
}
if ( diff . command = = Diff : : Delete ) {
leftLines . last ( ) + = line ;
currentLeftPos + = line . count ( ) ;
2013-07-17 00:01:45 +03:00
} else if ( diff . command = = Diff : : Insert ) {
2013-02-15 12:49:50 +01:00
rightLines . last ( ) + = line ;
currentRightPos + = line . count ( ) ;
2013-07-17 00:01:45 +03:00
} else if ( diff . command = = Diff : : Equal ) {
2014-01-15 09:50:23 +01:00
if ( ( line . count ( ) | | ( j & & j < lines . count ( ) - 1 ) | | ( i = = diffList . count ( ) ) ) & & // don't treat empty ending line as a line to be aligned unless a line is a one char '\n' only or it's the last line.
2013-02-15 12:49:50 +01:00
currentLeftLine ! = lastAlignedLeftLine & &
currentRightLine ! = lastAlignedRightLine ) {
// apply line spans before the current lines
if ( currentLeftLineOffset < currentRightLineOffset ) {
const int spans = currentRightLineOffset - currentLeftLineOffset ;
leftLineSpans [ currentLeftLine ] = spans ;
} else if ( currentRightLineOffset < currentLeftLineOffset ) {
const int spans = currentLeftLineOffset - currentRightLineOffset ;
rightLineSpans [ currentRightLine ] = spans ;
}
currentLeftLineOffset = 0 ;
currentRightLineOffset = 0 ;
lastAlignedLeftLine = currentLeftLine ;
lastAlignedRightLine = currentRightLine ;
}
leftLines . last ( ) + = line ;
rightLines . last ( ) + = line ;
currentLeftPos + = line . count ( ) ;
currentRightPos + = line . count ( ) ;
}
}
if ( ! equal ) {
if ( diff . command = = Diff : : Delete & & lastLeftPos ! = currentLeftPos )
leftChangedPositions . insert ( lastLeftPos , currentLeftPos ) ;
else if ( diff . command = = Diff : : Insert & & lastRightPos ! = currentRightPos )
rightChangedPositions . insert ( lastRightPos , currentRightPos ) ;
}
}
if ( diffList . count ( ) & & diffList . last ( ) . command = = Diff : : Equal ) {
2013-02-21 17:14:07 +01:00
if ( currentLeftLine ! = lastAlignedLeftLine & &
currentRightLine ! = lastAlignedRightLine ) {
// apply line spans before the current lines
if ( currentLeftLineOffset < currentRightLineOffset ) {
const int spans = currentRightLineOffset - currentLeftLineOffset ;
leftLineSpans [ currentLeftLine ] = spans ;
} else if ( currentRightLineOffset < currentLeftLineOffset ) {
const int spans = currentLeftLineOffset - currentRightLineOffset ;
rightLineSpans [ currentRightLine ] = spans ;
}
}
2013-02-15 12:49:50 +01:00
if ( lastLeftLineEqual & & lastRightLineEqual ) {
leftEqualLines . append ( currentLeftLine ) ;
rightEqualLines . append ( currentRightLine ) ;
}
}
2013-04-23 14:35:49 +02:00
QList < TextLineData > leftData = assemblyRows ( leftLines ,
leftLineSpans ,
leftChangedPositions ,
& chunkData . changedLeftPositions ) ;
QList < TextLineData > rightData = assemblyRows ( rightLines ,
rightLineSpans ,
rightChangedPositions ,
& chunkData . changedRightPositions ) ;
2013-02-15 12:49:50 +01:00
// fill ending separators
for ( int i = leftData . count ( ) ; i < rightData . count ( ) ; i + + )
leftData . append ( TextLineData ( TextLineData : : Separator ) ) ;
for ( int i = rightData . count ( ) ; i < leftData . count ( ) ; i + + )
rightData . append ( TextLineData ( TextLineData : : Separator ) ) ;
const int visualLineCount = leftData . count ( ) ;
int l = 0 ;
int r = 0 ;
for ( int i = 0 ; i < visualLineCount ; i + + ) {
RowData row ( leftData . at ( i ) , rightData . at ( i ) ) ;
if ( row . leftLine . textLineType = = TextLineData : : Separator
& & row . rightLine . textLineType = = TextLineData : : Separator )
row . equal = true ;
if ( row . leftLine . textLineType = = TextLineData : : TextLine
& & row . rightLine . textLineType = = TextLineData : : TextLine
& & leftEqualLines . contains ( l )
& & rightEqualLines . contains ( r ) )
row . equal = true ;
chunkData . rows . append ( row ) ;
if ( leftData . at ( i ) . textLineType = = TextLineData : : TextLine )
l + + ;
if ( rightData . at ( i ) . textLineType = = TextLineData : : TextLine )
r + + ;
}
return chunkData ;
}
FileData DiffEditorWidget : : calculateContextData ( const ChunkData & originalData ) const
{
2013-12-16 16:19:40 +01:00
const int contextLinesNumber = m_controller ? m_controller - > contextLinesNumber ( ) : 3 ;
if ( contextLinesNumber < 0 )
2013-02-15 12:49:50 +01:00
return FileData ( originalData ) ;
2013-04-10 13:51:04 +02:00
const int joinChunkThreshold = 1 ;
2013-02-15 12:49:50 +01:00
FileData fileData ;
QMap < int , bool > hiddenRows ;
int i = 0 ;
while ( i < originalData . rows . count ( ) ) {
const RowData & row = originalData . rows [ i ] ;
if ( row . equal ) {
// count how many equal
int equalRowStart = i ;
i + + ;
while ( i < originalData . rows . count ( ) ) {
if ( ! originalData . rows . at ( i ) . equal )
break ;
i + + ;
}
const bool first = equalRowStart = = 0 ; // includes first line?
const bool last = i = = originalData . rows . count ( ) ; // includes last line?
2013-12-16 16:19:40 +01:00
const int firstLine = first ? 0 : equalRowStart + contextLinesNumber ;
const int lastLine = last ? originalData . rows . count ( ) : i - contextLinesNumber ;
2013-02-15 12:49:50 +01:00
if ( firstLine < lastLine - joinChunkThreshold ) {
for ( int j = firstLine ; j < lastLine ; j + + ) {
hiddenRows . insert ( j , true ) ;
}
}
} else {
// iterate to the next row
i + + ;
}
}
i = 0 ;
int leftCharCounter = 0 ;
int rightCharCounter = 0 ;
QMap < int , int > : : ConstIterator leftChangedIt = originalData . changedLeftPositions . constBegin ( ) ;
QMap < int , int > : : ConstIterator rightChangedIt = originalData . changedRightPositions . constBegin ( ) ;
while ( i < originalData . rows . count ( ) ) {
if ( ! hiddenRows . contains ( i ) ) {
ChunkData chunkData ;
2013-04-09 10:26:31 +02:00
int leftOffset = leftCharCounter ;
int rightOffset = rightCharCounter ;
2013-04-23 13:16:52 +02:00
chunkData . contextChunk = false ;
2013-02-15 12:49:50 +01:00
while ( i < originalData . rows . count ( ) ) {
if ( hiddenRows . contains ( i ) )
break ;
RowData rowData = originalData . rows . at ( i ) ;
chunkData . rows . append ( rowData ) ;
2013-05-07 14:02:08 +02:00
leftCharCounter + = rowData . leftLine . text . count ( ) + 1 ; // +1 for '\n'
rightCharCounter + = rowData . rightLine . text . count ( ) + 1 ; // +1 for '\n'
2013-02-15 12:49:50 +01:00
i + + ;
}
while ( leftChangedIt ! = originalData . changedLeftPositions . constEnd ( ) ) {
2013-04-09 10:26:31 +02:00
if ( leftChangedIt . key ( ) < leftOffset
2013-02-15 12:49:50 +01:00
| | leftChangedIt . key ( ) > leftCharCounter )
break ;
2013-04-09 10:26:31 +02:00
const int startPos = leftChangedIt . key ( ) ;
const int endPos = leftChangedIt . value ( ) ;
2013-04-25 17:37:20 +02:00
chunkData . changedLeftPositions . insert ( startPos - leftOffset , endPos - leftOffset ) ;
2013-02-15 12:49:50 +01:00
leftChangedIt + + ;
}
while ( rightChangedIt ! = originalData . changedRightPositions . constEnd ( ) ) {
2013-04-09 10:26:31 +02:00
if ( rightChangedIt . key ( ) < rightOffset
2013-02-15 12:49:50 +01:00
| | rightChangedIt . key ( ) > rightCharCounter )
break ;
2013-04-09 10:26:31 +02:00
const int startPos = rightChangedIt . key ( ) ;
const int endPos = rightChangedIt . value ( ) ;
2013-04-25 17:37:20 +02:00
chunkData . changedRightPositions . insert ( startPos - rightOffset , endPos - rightOffset ) ;
2013-02-15 12:49:50 +01:00
rightChangedIt + + ;
}
fileData . chunks . append ( chunkData ) ;
} else {
2013-04-09 10:26:31 +02:00
ChunkData chunkData ;
2013-04-23 13:16:52 +02:00
chunkData . contextChunk = true ;
2013-04-09 10:26:31 +02:00
while ( i < originalData . rows . count ( ) ) {
if ( ! hiddenRows . contains ( i ) )
break ;
RowData rowData = originalData . rows . at ( i ) ;
chunkData . rows . append ( rowData ) ;
2013-02-15 12:49:50 +01:00
2013-05-07 14:02:08 +02:00
leftCharCounter + = rowData . leftLine . text . count ( ) + 1 ; // +1 for '\n'
rightCharCounter + = rowData . rightLine . text . count ( ) + 1 ; // +1 for '\n'
2013-04-09 10:26:31 +02:00
i + + ;
}
fileData . chunks . append ( chunkData ) ;
}
2013-02-15 12:49:50 +01:00
}
return fileData ;
}
void DiffEditorWidget : : showDiff ( )
{
2013-04-09 10:26:31 +02:00
// TODO: remember the line number of the line in the middle
2013-02-15 12:49:50 +01:00
const int verticalValue = m_leftEditor - > verticalScrollBar ( ) - > value ( ) ;
const int leftHorizontalValue = m_leftEditor - > horizontalScrollBar ( ) - > value ( ) ;
const int rightHorizontalValue = m_rightEditor - > horizontalScrollBar ( ) - > value ( ) ;
2013-12-16 16:19:40 +01:00
clear ( tr ( " No difference " ) ) ;
2013-02-15 12:49:50 +01:00
2013-12-16 16:19:40 +01:00
QList < QPair < DiffEditorController : : DiffFileInfo , QString > > leftDocs , rightDocs ;
2013-08-14 13:52:13 +02:00
QString leftTexts , rightTexts ;
2013-04-09 10:26:31 +02:00
int blockNumber = 0 ;
QChar separator = QLatin1Char ( ' \n ' ) ;
2013-04-25 17:37:20 +02:00
for ( int i = 0 ; i < m_contextFileData . count ( ) ; i + + ) {
2013-08-14 13:52:13 +02:00
QString leftText , rightText ;
2013-04-25 17:37:20 +02:00
const FileData & contextFileData = m_contextFileData . at ( i ) ;
int leftLineNumber = 0 ;
int rightLineNumber = 0 ;
2013-05-07 14:02:08 +02:00
m_leftEditor - > setFileInfo ( blockNumber , contextFileData . leftFileInfo ) ;
m_rightEditor - > setFileInfo ( blockNumber , contextFileData . rightFileInfo ) ;
2013-08-14 13:52:13 +02:00
leftText = separator ;
rightText = separator ;
2013-04-25 17:37:20 +02:00
blockNumber + + ;
for ( int j = 0 ; j < contextFileData . chunks . count ( ) ; j + + ) {
ChunkData chunkData = contextFileData . chunks . at ( j ) ;
if ( chunkData . contextChunk ) {
const int skippedLines = chunkData . rows . count ( ) ;
m_leftEditor - > setSkippedLines ( blockNumber , skippedLines ) ;
m_rightEditor - > setSkippedLines ( blockNumber , skippedLines ) ;
leftText + = separator ;
rightText + = separator ;
blockNumber + + ;
2013-02-15 12:49:50 +01:00
}
2013-04-25 17:37:20 +02:00
for ( int k = 0 ; k < chunkData . rows . count ( ) ; k + + ) {
RowData rowData = chunkData . rows . at ( k ) ;
TextLineData leftLineData = rowData . leftLine ;
TextLineData rightLineData = rowData . rightLine ;
if ( leftLineData . textLineType = = TextLineData : : TextLine ) {
leftText + = leftLineData . text ;
leftLineNumber + + ;
2013-04-26 11:50:10 +02:00
m_leftEditor - > setLineNumber ( blockNumber , leftLineNumber ) ;
2013-04-25 17:37:20 +02:00
} else if ( leftLineData . textLineType = = TextLineData : : Separator ) {
m_leftEditor - > setSeparator ( blockNumber , true ) ;
}
if ( rightLineData . textLineType = = TextLineData : : TextLine ) {
rightText + = rightLineData . text ;
rightLineNumber + + ;
2013-04-26 11:50:10 +02:00
m_rightEditor - > setLineNumber ( blockNumber , rightLineNumber ) ;
2013-04-25 17:37:20 +02:00
} else if ( rightLineData . textLineType = = TextLineData : : Separator ) {
m_rightEditor - > setSeparator ( blockNumber , true ) ;
}
leftText + = separator ;
rightText + = separator ;
blockNumber + + ;
2013-02-15 12:49:50 +01:00
}
}
2013-08-14 13:52:13 +02:00
leftTexts + = leftText ;
rightTexts + = rightText ;
leftDocs . append ( qMakePair ( contextFileData . leftFileInfo , leftText ) ) ;
rightDocs . append ( qMakePair ( contextFileData . rightFileInfo , rightText ) ) ;
2013-02-15 12:49:50 +01:00
}
2013-05-07 14:02:08 +02:00
2013-08-14 13:52:13 +02:00
if ( leftTexts . isEmpty ( ) & & rightTexts . isEmpty ( ) )
2013-05-07 14:02:08 +02:00
return ;
2013-02-15 12:49:50 +01:00
2013-08-14 13:52:13 +02:00
m_leftEditor - > setDocuments ( leftDocs ) ;
m_rightEditor - > setDocuments ( rightDocs ) ;
m_leftEditor - > setPlainText ( leftTexts ) ;
m_rightEditor - > setPlainText ( rightTexts ) ;
2013-02-15 12:49:50 +01:00
colorDiff ( m_contextFileData ) ;
2013-04-09 10:26:31 +02:00
2013-04-25 17:37:20 +02:00
QTextBlock leftBlock = m_leftEditor - > document ( ) - > firstBlock ( ) ;
QTextBlock rightBlock = m_rightEditor - > document ( ) - > firstBlock ( ) ;
for ( int i = 0 ; i < m_contextFileData . count ( ) ; i + + ) {
const FileData & contextFileData = m_contextFileData . at ( i ) ;
leftBlock = leftBlock . next ( ) ;
rightBlock = rightBlock . next ( ) ;
for ( int j = 0 ; j < contextFileData . chunks . count ( ) ; j + + ) {
ChunkData chunkData = contextFileData . chunks . at ( j ) ;
if ( chunkData . contextChunk ) {
TextEditor : : BaseTextDocumentLayout : : setFoldingIndent ( leftBlock , FILE_LEVEL ) ;
TextEditor : : BaseTextDocumentLayout : : setFoldingIndent ( rightBlock , FILE_LEVEL ) ;
2013-04-09 10:26:31 +02:00
leftBlock = leftBlock . next ( ) ;
2013-04-25 17:37:20 +02:00
rightBlock = rightBlock . next ( ) ;
2013-04-09 10:26:31 +02:00
}
2013-04-25 17:37:20 +02:00
const int indent = chunkData . contextChunk ? CHUNK_LEVEL : FILE_LEVEL ;
for ( int k = 0 ; k < chunkData . rows . count ( ) ; k + + ) {
TextEditor : : BaseTextDocumentLayout : : setFoldingIndent ( leftBlock , indent ) ;
TextEditor : : BaseTextDocumentLayout : : setFoldingIndent ( rightBlock , indent ) ;
leftBlock = leftBlock . next ( ) ;
2013-04-09 10:26:31 +02:00
rightBlock = rightBlock . next ( ) ;
}
}
}
2013-04-10 13:51:04 +02:00
blockNumber = 0 ;
2013-04-25 17:37:20 +02:00
for ( int i = 0 ; i < m_contextFileData . count ( ) ; i + + ) {
const FileData & contextFileData = m_contextFileData . at ( i ) ;
blockNumber + + ;
for ( int j = 0 ; j < contextFileData . chunks . count ( ) ; j + + ) {
ChunkData chunkData = contextFileData . chunks . at ( j ) ;
if ( chunkData . contextChunk ) {
QTextBlock leftBlock = m_leftEditor - > document ( ) - > findBlockByNumber ( blockNumber ) ;
TextEditor : : BaseTextDocumentLayout : : doFoldOrUnfold ( leftBlock , false ) ;
QTextBlock rightBlock = m_rightEditor - > document ( ) - > findBlockByNumber ( blockNumber ) ;
TextEditor : : BaseTextDocumentLayout : : doFoldOrUnfold ( rightBlock , false ) ;
blockNumber + + ;
}
blockNumber + = chunkData . rows . count ( ) ;
2013-04-10 13:51:04 +02:00
}
}
m_foldingBlocker = true ;
2013-04-09 10:26:31 +02:00
BaseTextDocumentLayout * leftLayout = qobject_cast < BaseTextDocumentLayout * > ( m_leftEditor - > document ( ) - > documentLayout ( ) ) ;
if ( leftLayout ) {
leftLayout - > requestUpdate ( ) ;
leftLayout - > emitDocumentSizeChanged ( ) ;
}
BaseTextDocumentLayout * rightLayout = qobject_cast < BaseTextDocumentLayout * > ( m_rightEditor - > document ( ) - > documentLayout ( ) ) ;
if ( rightLayout ) {
rightLayout - > requestUpdate ( ) ;
rightLayout - > emitDocumentSizeChanged ( ) ;
}
2013-04-10 13:51:04 +02:00
m_foldingBlocker = false ;
2013-04-09 10:26:31 +02:00
2013-02-15 12:49:50 +01:00
m_leftEditor - > verticalScrollBar ( ) - > setValue ( verticalValue ) ;
m_rightEditor - > verticalScrollBar ( ) - > setValue ( verticalValue ) ;
m_leftEditor - > horizontalScrollBar ( ) - > setValue ( leftHorizontalValue ) ;
m_rightEditor - > horizontalScrollBar ( ) - > setValue ( rightHorizontalValue ) ;
2013-04-25 17:37:20 +02:00
m_leftEditor - > updateFoldingHighlight ( QPoint ( - 1 , - 1 ) ) ;
m_rightEditor - > updateFoldingHighlight ( QPoint ( - 1 , - 1 ) ) ;
2013-02-15 12:49:50 +01:00
}
QList < QTextEdit : : ExtraSelection > DiffEditorWidget : : colorPositions (
const QTextCharFormat & format ,
QTextCursor & cursor ,
const QMap < int , int > & positions ) const
{
QList < QTextEdit : : ExtraSelection > lineSelections ;
cursor . setPosition ( 0 ) ;
QMapIterator < int , int > itPositions ( positions ) ;
while ( itPositions . hasNext ( ) ) {
itPositions . next ( ) ;
cursor . setPosition ( itPositions . key ( ) ) ;
cursor . setPosition ( itPositions . value ( ) , QTextCursor : : KeepAnchor ) ;
QTextEdit : : ExtraSelection selection ;
selection . cursor = cursor ;
selection . format = format ;
lineSelections . append ( selection ) ;
}
return lineSelections ;
}
2013-04-25 17:37:20 +02:00
void DiffEditorWidget : : colorDiff ( const QList < FileData > & fileDataList )
2013-02-15 12:49:50 +01:00
{
QPalette pal = m_leftEditor - > extraArea ( ) - > palette ( ) ;
pal . setCurrentColorGroup ( QPalette : : Active ) ;
QTextCharFormat spanLineFormat ;
spanLineFormat . setBackground ( pal . color ( QPalette : : Background ) ) ;
spanLineFormat . setProperty ( QTextFormat : : FullWidthSelection , true ) ;
int leftPos = 0 ;
int rightPos = 0 ;
2013-04-09 10:26:31 +02:00
QMap < int , int > leftLinePos ;
QMap < int , int > rightLinePos ;
QMap < int , int > leftCharPos ;
QMap < int , int > rightCharPos ;
2013-02-15 12:49:50 +01:00
QMap < int , int > leftSkippedPos ;
QMap < int , int > rightSkippedPos ;
QMap < int , int > leftChunkPos ;
QMap < int , int > rightChunkPos ;
2013-04-25 17:37:20 +02:00
QMap < int , int > leftFilePos ;
QMap < int , int > rightFilePos ;
2013-02-15 12:49:50 +01:00
int leftLastDiffBlockStartPos = 0 ;
int rightLastDiffBlockStartPos = 0 ;
int leftLastSkippedBlockStartPos = 0 ;
int rightLastSkippedBlockStartPos = 0 ;
2013-04-09 10:26:31 +02:00
2013-04-25 17:37:20 +02:00
for ( int i = 0 ; i < fileDataList . count ( ) ; i + + ) {
leftFilePos [ leftPos ] = leftPos + 1 ;
rightFilePos [ rightPos ] = rightPos + 1 ;
leftPos + + ; // for file line
rightPos + + ; // for file line
const FileData & fileData = fileDataList . at ( i ) ;
for ( int j = 0 ; j < fileData . chunks . count ( ) ; j + + ) {
ChunkData chunkData = fileData . chunks . at ( j ) ;
if ( chunkData . contextChunk ) {
leftChunkPos [ leftPos ] = leftPos + 1 ;
rightChunkPos [ rightPos ] = rightPos + 1 ;
leftPos + + ; // for chunk line
rightPos + + ; // for chunk line
}
const int leftFileOffset = leftPos ;
const int rightFileOffset = rightPos ;
leftLastDiffBlockStartPos = leftPos ;
rightLastDiffBlockStartPos = rightPos ;
leftLastSkippedBlockStartPos = leftPos ;
rightLastSkippedBlockStartPos = rightPos ;
QMapIterator < int , int > itLeft ( chunkData . changedLeftPositions ) ;
while ( itLeft . hasNext ( ) ) {
itLeft . next ( ) ;
leftCharPos [ itLeft . key ( ) + leftFileOffset ] = itLeft . value ( ) + leftFileOffset ;
}
2013-04-09 10:26:31 +02:00
2013-04-25 17:37:20 +02:00
QMapIterator < int , int > itRight ( chunkData . changedRightPositions ) ;
while ( itRight . hasNext ( ) ) {
itRight . next ( ) ;
2013-04-09 10:26:31 +02:00
2013-04-25 17:37:20 +02:00
rightCharPos [ itRight . key ( ) + rightFileOffset ] = itRight . value ( ) + rightFileOffset ;
}
2013-04-09 10:26:31 +02:00
2013-04-25 17:37:20 +02:00
for ( int k = 0 ; k < chunkData . rows . count ( ) ; k + + ) {
RowData rowData = chunkData . rows . at ( k ) ;
2013-02-15 12:49:50 +01:00
2013-05-07 14:02:08 +02:00
leftPos + = rowData . leftLine . text . count ( ) + 1 ; // +1 for '\n'
rightPos + = rowData . rightLine . text . count ( ) + 1 ; // +1 for '\n'
2013-02-15 12:49:50 +01:00
2013-04-25 17:37:20 +02:00
if ( ! rowData . equal ) {
if ( rowData . leftLine . textLineType = = TextLineData : : TextLine ) {
leftLinePos [ leftLastDiffBlockStartPos ] = leftPos ;
leftLastSkippedBlockStartPos = leftPos ;
} else {
leftSkippedPos [ leftLastSkippedBlockStartPos ] = leftPos ;
leftLastDiffBlockStartPos = leftPos ;
}
if ( rowData . rightLine . textLineType = = TextLineData : : TextLine ) {
rightLinePos [ rightLastDiffBlockStartPos ] = rightPos ;
rightLastSkippedBlockStartPos = rightPos ;
} else {
rightSkippedPos [ rightLastSkippedBlockStartPos ] = rightPos ;
rightLastDiffBlockStartPos = rightPos ;
}
2013-02-15 12:49:50 +01:00
} else {
leftLastDiffBlockStartPos = leftPos ;
rightLastDiffBlockStartPos = rightPos ;
2013-04-25 17:37:20 +02:00
leftLastSkippedBlockStartPos = leftPos ;
rightLastSkippedBlockStartPos = rightPos ;
2013-02-15 12:49:50 +01:00
}
}
}
}
QTextCursor leftCursor = m_leftEditor - > textCursor ( ) ;
QTextCursor rightCursor = m_rightEditor - > textCursor ( ) ;
QList < QTextEdit : : ExtraSelection > leftSelections
2013-06-05 15:25:42 +02:00
= colorPositions ( m_leftLineFormat , leftCursor , leftLinePos ) ;
2013-02-15 12:49:50 +01:00
leftSelections
+ = colorPositions ( spanLineFormat , leftCursor , leftSkippedPos ) ;
leftSelections
2013-06-05 15:25:42 +02:00
+ = colorPositions ( m_chunkLineFormat , leftCursor , leftChunkPos ) ;
2013-04-25 17:37:20 +02:00
leftSelections
2013-06-05 15:25:42 +02:00
+ = colorPositions ( m_fileLineFormat , leftCursor , leftFilePos ) ;
2013-04-09 10:26:31 +02:00
leftSelections
2013-06-05 15:25:42 +02:00
+ = colorPositions ( m_leftCharFormat , leftCursor , leftCharPos ) ;
2013-02-15 12:49:50 +01:00
QList < QTextEdit : : ExtraSelection > rightSelections
2013-06-05 15:25:42 +02:00
= colorPositions ( m_rightLineFormat , rightCursor , rightLinePos ) ;
2013-02-15 12:49:50 +01:00
rightSelections
+ = colorPositions ( spanLineFormat , rightCursor , rightSkippedPos ) ;
rightSelections
2013-06-05 15:25:42 +02:00
+ = colorPositions ( m_chunkLineFormat , rightCursor , rightChunkPos ) ;
2013-04-25 17:37:20 +02:00
rightSelections
2013-06-05 15:25:42 +02:00
+ = colorPositions ( m_fileLineFormat , rightCursor , rightFilePos ) ;
2013-04-09 10:26:31 +02:00
rightSelections
2013-06-05 15:25:42 +02:00
+ = colorPositions ( m_rightCharFormat , rightCursor , rightCharPos ) ;
m_leftEditor - > setExtraSelections ( BaseTextEditorWidget : : OtherSelection , leftSelections ) ;
m_rightEditor - > setExtraSelections ( BaseTextEditorWidget : : OtherSelection , rightSelections ) ;
}
static QTextCharFormat fullWidthFormatForTextStyle ( const TextEditor : : FontSettings & fontSettings ,
TextEditor : : TextStyle textStyle )
{
QTextCharFormat format = fontSettings . toTextCharFormat ( textStyle ) ;
format . setProperty ( QTextFormat : : FullWidthSelection , true ) ;
return format ;
}
void DiffEditorWidget : : setFontSettings ( const TextEditor : : FontSettings & fontSettings )
{
2014-01-21 11:26:39 +01:00
m_leftEditor - > baseTextDocument ( ) - > setFontSettings ( fontSettings ) ;
m_rightEditor - > baseTextDocument ( ) - > setFontSettings ( fontSettings ) ;
2013-06-05 15:25:42 +02:00
m_fileLineFormat = fullWidthFormatForTextStyle ( fontSettings , C_DIFF_FILE_LINE ) ;
m_chunkLineFormat = fullWidthFormatForTextStyle ( fontSettings , C_DIFF_CONTEXT_LINE ) ;
m_leftLineFormat = fullWidthFormatForTextStyle ( fontSettings , C_DIFF_SOURCE_LINE ) ;
m_leftCharFormat = fullWidthFormatForTextStyle ( fontSettings , C_DIFF_SOURCE_CHAR ) ;
m_rightLineFormat = fullWidthFormatForTextStyle ( fontSettings , C_DIFF_DEST_LINE ) ;
m_rightCharFormat = fullWidthFormatForTextStyle ( fontSettings , C_DIFF_DEST_CHAR ) ;
colorDiff ( m_contextFileData ) ;
2013-02-15 12:49:50 +01:00
}
2013-07-01 13:15:27 +02:00
void DiffEditorWidget : : slotLeftJumpToOriginalFileRequested ( int diffFileIndex ,
int lineNumber ,
int columnNumber )
{
if ( diffFileIndex < 0 | | diffFileIndex > = m_contextFileData . count ( ) )
return ;
const FileData fileData = m_contextFileData . at ( diffFileIndex ) ;
const QString leftFileName = fileData . leftFileInfo . fileName ;
const QString rightFileName = fileData . rightFileInfo . fileName ;
if ( leftFileName = = rightFileName ) {
// The same file (e.g. in git diff), jump to the line number taken from the right editor.
// Warning: git show SHA^ vs SHA or git diff HEAD vs Index
// (when Working tree has changed in meantime) will not work properly.
int leftLineNumber = 0 ;
int rightLineNumber = 0 ;
for ( int i = 0 ; i < fileData . chunks . count ( ) ; i + + ) {
const ChunkData chunkData = fileData . chunks . at ( i ) ;
for ( int j = 0 ; j < chunkData . rows . count ( ) ; j + + ) {
const RowData rowData = chunkData . rows . at ( j ) ;
if ( rowData . leftLine . textLineType = = TextLineData : : TextLine )
leftLineNumber + + ;
if ( rowData . rightLine . textLineType = = TextLineData : : TextLine )
rightLineNumber + + ;
if ( leftLineNumber = = lineNumber ) {
int colNr = rowData . equal ? columnNumber : 0 ;
jumpToOriginalFile ( leftFileName , rightLineNumber , colNr ) ;
return ;
}
}
}
} else {
// different file (e.g. in Tools | Diff...)
jumpToOriginalFile ( leftFileName , lineNumber , columnNumber ) ;
}
}
void DiffEditorWidget : : slotRightJumpToOriginalFileRequested ( int diffFileIndex ,
int lineNumber , int columnNumber )
{
if ( diffFileIndex < 0 | | diffFileIndex > = m_contextFileData . count ( ) )
return ;
const FileData fileData = m_contextFileData . at ( diffFileIndex ) ;
const QString fileName = fileData . rightFileInfo . fileName ;
jumpToOriginalFile ( fileName , lineNumber , columnNumber ) ;
}
void DiffEditorWidget : : jumpToOriginalFile ( const QString & fileName ,
int lineNumber , int columnNumber )
{
2013-12-16 16:19:40 +01:00
if ( ! m_controller )
return ;
const QDir dir ( m_controller - > workingDirectory ( ) ) ;
2013-07-01 13:15:27 +02:00
const QString absoluteFileName = dir . absoluteFilePath ( fileName ) ;
Core : : EditorManager : : openEditorAt ( absoluteFileName , lineNumber , columnNumber ) ;
}
2013-05-22 16:33:44 +02:00
void DiffEditorWidget : : leftVSliderChanged ( )
2013-02-15 12:49:50 +01:00
{
m_rightEditor - > verticalScrollBar ( ) - > setValue ( m_leftEditor - > verticalScrollBar ( ) - > value ( ) ) ;
}
2013-05-22 16:33:44 +02:00
void DiffEditorWidget : : rightVSliderChanged ( )
2013-02-15 12:49:50 +01:00
{
m_leftEditor - > verticalScrollBar ( ) - > setValue ( m_rightEditor - > verticalScrollBar ( ) - > value ( ) ) ;
}
2013-05-22 16:33:44 +02:00
void DiffEditorWidget : : leftHSliderChanged ( )
{
2013-12-16 16:19:40 +01:00
if ( ! m_controller | | m_controller - > horizontalScrollBarSynchronization ( ) )
2013-05-22 16:33:44 +02:00
m_rightEditor - > horizontalScrollBar ( ) - > setValue ( m_leftEditor - > horizontalScrollBar ( ) - > value ( ) ) ;
}
void DiffEditorWidget : : rightHSliderChanged ( )
{
2013-12-16 16:19:40 +01:00
if ( ! m_controller | | m_controller - > horizontalScrollBarSynchronization ( ) )
2013-05-22 16:33:44 +02:00
m_leftEditor - > horizontalScrollBar ( ) - > setValue ( m_rightEditor - > horizontalScrollBar ( ) - > value ( ) ) ;
}
2013-05-23 13:36:27 +02:00
void DiffEditorWidget : : leftCursorPositionChanged ( )
{
leftVSliderChanged ( ) ;
leftHSliderChanged ( ) ;
2013-12-16 16:19:40 +01:00
if ( ! m_controller )
return ;
m_controller - > setCurrentDiffFileIndex ( m_leftEditor - > fileIndexForBlockNumber ( m_leftEditor - > textCursor ( ) . blockNumber ( ) ) ) ;
2013-05-23 13:36:27 +02:00
}
void DiffEditorWidget : : rightCursorPositionChanged ( )
{
rightVSliderChanged ( ) ;
rightHSliderChanged ( ) ;
2013-12-16 16:19:40 +01:00
if ( ! m_controller )
return ;
m_controller - > setCurrentDiffFileIndex ( m_rightEditor - > fileIndexForBlockNumber ( m_rightEditor - > textCursor ( ) . blockNumber ( ) ) ) ;
2013-05-23 13:36:27 +02:00
}
2013-04-10 13:51:04 +02:00
void DiffEditorWidget : : leftDocumentSizeChanged ( )
{
synchronizeFoldings ( m_leftEditor , m_rightEditor ) ;
}
void DiffEditorWidget : : rightDocumentSizeChanged ( )
{
synchronizeFoldings ( m_rightEditor , m_leftEditor ) ;
}
2013-04-25 17:37:20 +02:00
/* Special version of that method (original: TextEditor::BaseTextDocumentLayout::doFoldOrUnfold())
The hack lies in fact , that when unfolding all direct sub - blocks are made visible ,
while some of them need to stay invisible ( i . e . unfolded chunk lines )
*/
static void doFoldOrUnfold ( DiffViewEditorWidget * editor , const QTextBlock & block , bool unfold )
{
if ( ! TextEditor : : BaseTextDocumentLayout : : canFold ( block ) )
return ;
QTextBlock b = block . next ( ) ;
int indent = TextEditor : : BaseTextDocumentLayout : : foldingIndent ( block ) ;
while ( b . isValid ( ) & & TextEditor : : BaseTextDocumentLayout : : foldingIndent ( b ) > indent & & ( unfold | | b . next ( ) . isValid ( ) ) ) {
if ( unfold & & editor - > isChunkLine ( b . blockNumber ( ) ) & & ! TextEditor : : BaseTextDocumentLayout : : isFolded ( b ) ) {
b . setVisible ( false ) ;
b . setLineCount ( 0 ) ;
} else {
b . setVisible ( unfold ) ;
b . setLineCount ( unfold ? qMax ( 1 , b . layout ( ) - > lineCount ( ) ) : 0 ) ;
}
if ( unfold ) { // do not unfold folded sub-blocks
if ( TextEditor : : BaseTextDocumentLayout : : isFolded ( b ) & & b . next ( ) . isValid ( ) ) {
int jndent = TextEditor : : BaseTextDocumentLayout : : foldingIndent ( b ) ;
b = b . next ( ) ;
while ( b . isValid ( ) & & TextEditor : : BaseTextDocumentLayout : : foldingIndent ( b ) > jndent )
b = b . next ( ) ;
continue ;
}
}
b = b . next ( ) ;
}
TextEditor : : BaseTextDocumentLayout : : setFolded ( block , ! unfold ) ;
}
2013-04-10 13:51:04 +02:00
void DiffEditorWidget : : synchronizeFoldings ( DiffViewEditorWidget * source , DiffViewEditorWidget * destination )
{
if ( m_foldingBlocker )
return ;
m_foldingBlocker = true ;
QTextBlock sourceBlock = source - > document ( ) - > firstBlock ( ) ;
QTextBlock destinationBlock = destination - > document ( ) - > firstBlock ( ) ;
while ( sourceBlock . isValid ( ) & & destinationBlock . isValid ( ) ) {
if ( TextEditor : : BaseTextDocumentLayout : : canFold ( sourceBlock ) ) {
const bool isSourceFolded = TextEditor : : BaseTextDocumentLayout : : isFolded ( sourceBlock ) ;
const bool isDestinationFolded = TextEditor : : BaseTextDocumentLayout : : isFolded ( destinationBlock ) ;
if ( isSourceFolded ! = isDestinationFolded ) {
2013-04-25 17:37:20 +02:00
if ( source - > isFileLine ( sourceBlock . blockNumber ( ) ) ) {
doFoldOrUnfold ( source , sourceBlock , ! isSourceFolded ) ;
doFoldOrUnfold ( destination , destinationBlock , ! isSourceFolded ) ;
} else {
if ( isSourceFolded ) { // we fold the destination (shrinking)
QTextBlock previousSource = sourceBlock . previous ( ) ; // skippedLines
QTextBlock previousDestination = destinationBlock . previous ( ) ; // skippedLines
if ( source - > isChunkLine ( previousSource . blockNumber ( ) ) ) {
QTextBlock firstVisibleDestinationBlock = destination - > firstVisibleBlock ( ) ;
QTextBlock firstDestinationBlock = destination - > document ( ) - > firstBlock ( ) ;
TextEditor : : BaseTextDocumentLayout : : doFoldOrUnfold ( destinationBlock , ! isSourceFolded ) ;
TextEditor : : BaseTextDocumentLayout : : setFoldingIndent ( sourceBlock , CHUNK_LEVEL ) ;
TextEditor : : BaseTextDocumentLayout : : setFoldingIndent ( destinationBlock , CHUNK_LEVEL ) ;
previousSource . setVisible ( true ) ;
previousSource . setLineCount ( 1 ) ;
previousDestination . setVisible ( true ) ;
previousDestination . setLineCount ( 1 ) ;
sourceBlock . setVisible ( false ) ;
sourceBlock . setLineCount ( 0 ) ;
destinationBlock . setVisible ( false ) ;
destinationBlock . setLineCount ( 0 ) ;
TextEditor : : BaseTextDocumentLayout : : setFolded ( previousSource , true ) ;
TextEditor : : BaseTextDocumentLayout : : setFolded ( previousDestination , true ) ;
if ( firstVisibleDestinationBlock = = destinationBlock ) {
/*
The following hack is completely crazy . That ' s the only way to scroll 1 line up
in case destinationBlock was the top visible block .
There is no need to scroll the source since this is in sync anyway
( leftSliderChanged ( ) , rightSliderChanged ( ) )
*/
destination - > verticalScrollBar ( ) - > setValue ( destination - > verticalScrollBar ( ) - > value ( ) - 1 ) ;
destination - > verticalScrollBar ( ) - > setValue ( destination - > verticalScrollBar ( ) - > value ( ) + 1 ) ;
if ( firstVisibleDestinationBlock . previous ( ) = = firstDestinationBlock ) {
/*
Even more crazy case : the destinationBlock was the first top visible block .
*/
destination - > verticalScrollBar ( ) - > setValue ( 0 ) ;
}
}
}
} else { // we unfold the destination (expanding)
if ( source - > isChunkLine ( sourceBlock . blockNumber ( ) ) ) {
QTextBlock nextSource = sourceBlock . next ( ) ;
QTextBlock nextDestination = destinationBlock . next ( ) ;
TextEditor : : BaseTextDocumentLayout : : doFoldOrUnfold ( destinationBlock , ! isSourceFolded ) ;
TextEditor : : BaseTextDocumentLayout : : setFoldingIndent ( nextSource , FILE_LEVEL ) ;
TextEditor : : BaseTextDocumentLayout : : setFoldingIndent ( nextDestination , FILE_LEVEL ) ;
sourceBlock . setVisible ( false ) ;
sourceBlock . setLineCount ( 0 ) ;
destinationBlock . setVisible ( false ) ;
destinationBlock . setLineCount ( 0 ) ;
TextEditor : : BaseTextDocumentLayout : : setFolded ( nextSource , false ) ;
TextEditor : : BaseTextDocumentLayout : : setFolded ( nextDestination , false ) ;
2013-04-10 13:51:04 +02:00
}
}
}
break ; // only one should be synchronized
}
}
sourceBlock = sourceBlock . next ( ) ;
destinationBlock = destinationBlock . next ( ) ;
}
BaseTextDocumentLayout * sourceLayout = qobject_cast < BaseTextDocumentLayout * > ( source - > document ( ) - > documentLayout ( ) ) ;
if ( sourceLayout ) {
sourceLayout - > requestUpdate ( ) ;
sourceLayout - > emitDocumentSizeChanged ( ) ;
}
2013-05-22 15:11:35 +02:00
QWidget * ea = source - > extraArea ( ) ;
if ( ea - > contentsRect ( ) . contains ( ea - > mapFromGlobal ( QCursor : : pos ( ) ) ) )
source - > updateFoldingHighlight ( source - > mapFromGlobal ( QCursor : : pos ( ) ) ) ;
2013-04-10 13:51:04 +02:00
BaseTextDocumentLayout * destinationLayout = qobject_cast < BaseTextDocumentLayout * > ( destination - > document ( ) - > documentLayout ( ) ) ;
if ( destinationLayout ) {
destinationLayout - > requestUpdate ( ) ;
destinationLayout - > emitDocumentSizeChanged ( ) ;
}
m_foldingBlocker = false ;
}
2013-02-15 12:49:50 +01:00
2013-02-21 17:03:00 +01:00
} // namespace DiffEditor
2013-02-15 12:49:50 +01:00
2013-06-07 15:23:12 +02:00
# ifdef WITH_TESTS
# include <QTest>
void DiffEditor : : DiffEditorWidget : : testAssemblyRows ( )
{
QStringList lines ;
lines < < QLatin1String ( " abcd efgh " ) ; // line 0
lines < < QLatin1String ( " ijkl mnop " ) ; // line 1
QMap < int , int > lineSpans ;
lineSpans [ 1 ] = 6 ; // before line 1 insert 6 span lines
QMap < int , int > changedPositions ;
changedPositions [ 5 ] = 14 ; // changed text from position 5 to position 14, occupy 9 characters: "efgh\nijkl"
QMap < int , int > expectedChangedPositions ;
expectedChangedPositions [ 5 ] = 20 ; // "efgh\n[\n\n\n\n\n\n]ijkl" - [\n] means inserted span
QMap < int , int > outputChangedPositions ;
assemblyRows ( lines , lineSpans , changedPositions , & outputChangedPositions ) ;
QVERIFY ( outputChangedPositions = = expectedChangedPositions ) ;
}
# endif // WITH_TESTS
2013-02-15 12:49:50 +01:00
# include "diffeditorwidget.moc"