961 lines
22 KiB
C++
961 lines
22 KiB
C++
#include "TextBuffer.h"
|
|
#include <QDataStream>
|
|
#include <QFile>
|
|
#include <QTextCodec>
|
|
#include <QTextStream>
|
|
#include <stdexcept>
|
|
#include "SynEdit.h"
|
|
#include "../utils.h"
|
|
|
|
SynEditStringList::SynEditStringList(SynEdit *pEdit, QObject *parent):
|
|
QObject(parent),
|
|
mEdit(pEdit)
|
|
{
|
|
mAppendNewLineAtEOF = true;
|
|
mFileEndingType = FileEndingType::Windows;
|
|
mIndexOfLongestLine = -1;
|
|
mUpdateCount = 0;
|
|
}
|
|
|
|
static void ListIndexOutOfBounds(int index) {
|
|
throw IndexOutOfRange(index);
|
|
}
|
|
|
|
|
|
|
|
int SynEditStringList::parenthesisLevels(int Index)
|
|
{
|
|
if (Index>=0 && Index < mList.size()) {
|
|
return mList[Index]->fParenthesisLevel;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
int SynEditStringList::bracketLevels(int Index)
|
|
{
|
|
if (Index>=0 && Index < mList.size()) {
|
|
return mList[Index]->fBracketLevel;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
int SynEditStringList::braceLevels(int Index)
|
|
{
|
|
if (Index>=0 && Index < mList.size()) {
|
|
return mList[Index]->fBraceLevel;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
//QString SynEditStringList::expandedStrings(int Index)
|
|
//{
|
|
// if (Index>=0 && Index < mList.size()) {
|
|
// if (mList[Index]->fFlags & SynEditStringFlag::sfHasNoTabs)
|
|
// return mList[Index]->fString;
|
|
// else
|
|
// return ExpandString(Index);
|
|
// } else
|
|
// return QString();
|
|
//}
|
|
|
|
int SynEditStringList::lineColumns(int Index)
|
|
{
|
|
if (Index>=0 && Index < mList.size()) {
|
|
if (mList[Index]->fColumns == -1) {
|
|
return calculateLineColumns(Index);
|
|
} else
|
|
return mList[Index]->fColumns;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
int SynEditStringList::lengthOfLongestLine()
|
|
{
|
|
if (mIndexOfLongestLine < 0) {
|
|
int MaxLen = -1;
|
|
mIndexOfLongestLine = -1;
|
|
if (mList.count() > 0 ) {
|
|
for (int i=0;i<mList.size();i++) {
|
|
int len = lineColumns(i);
|
|
if (len > MaxLen) {
|
|
MaxLen = len;
|
|
mIndexOfLongestLine = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (mIndexOfLongestLine >= 0)
|
|
return mList[mIndexOfLongestLine]->fColumns;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
QString SynEditStringList::lineBreak()
|
|
{
|
|
switch(mFileEndingType) {
|
|
case FileEndingType::Linux:
|
|
return "\n";
|
|
case FileEndingType::Windows:
|
|
return "\r\n";
|
|
case FileEndingType::Mac:
|
|
return "\r";
|
|
}
|
|
return "\n";
|
|
}
|
|
|
|
SynRangeState SynEditStringList::ranges(int Index)
|
|
{
|
|
if (Index>=0 && Index < mList.size()) {
|
|
return mList[Index]->fRange;
|
|
} else
|
|
return {0,0};
|
|
}
|
|
|
|
void SynEditStringList::InsertItem(int Index, const QString &s)
|
|
{
|
|
beginUpdate();
|
|
PSynEditStringRec line = std::make_shared<SynEditStringRec>();
|
|
line->fString = s;
|
|
mIndexOfLongestLine = -1;
|
|
mList.insert(Index,line);
|
|
endUpdate();
|
|
}
|
|
|
|
void SynEditStringList::addItem(const QString &s)
|
|
{
|
|
beginUpdate();
|
|
PSynEditStringRec line = std::make_shared<SynEditStringRec>();
|
|
line->fString = s;
|
|
mIndexOfLongestLine = -1;
|
|
mList.append(line);
|
|
endUpdate();
|
|
}
|
|
|
|
ConvertTabsProcEx SynEditStringList::getConvertTabsProc() const
|
|
{
|
|
return mConvertTabsProc;
|
|
}
|
|
|
|
bool SynEditStringList::getAppendNewLineAtEOF() const
|
|
{
|
|
return mAppendNewLineAtEOF;
|
|
}
|
|
|
|
void SynEditStringList::setAppendNewLineAtEOF(bool appendNewLineAtEOF)
|
|
{
|
|
mAppendNewLineAtEOF = appendNewLineAtEOF;
|
|
}
|
|
|
|
void SynEditStringList::setRange(int Index, SynRangeState ARange)
|
|
{
|
|
if (Index<0 || Index>=mList.count()) {
|
|
ListIndexOutOfBounds(Index);
|
|
}
|
|
beginUpdate();
|
|
mList[Index]->fRange = ARange;
|
|
endUpdate();
|
|
}
|
|
|
|
void SynEditStringList::setParenthesisLevel(int Index, int level)
|
|
{
|
|
if (Index<0 || Index>=mList.count()) {
|
|
ListIndexOutOfBounds(Index);
|
|
}
|
|
beginUpdate();
|
|
mList[Index]->fParenthesisLevel = level;
|
|
endUpdate();
|
|
}
|
|
|
|
void SynEditStringList::setBracketLevel(int Index, int level)
|
|
{
|
|
if (Index<0 || Index>=mList.count()) {
|
|
ListIndexOutOfBounds(Index);
|
|
}
|
|
beginUpdate();
|
|
mList[Index]->fBracketLevel = level;
|
|
endUpdate();
|
|
}
|
|
|
|
void SynEditStringList::setBraceLevel(int Index, int level)
|
|
{
|
|
if (Index<0 || Index>=mList.count()) {
|
|
ListIndexOutOfBounds(Index);
|
|
}
|
|
beginUpdate();
|
|
mList[Index]->fBraceLevel = level;
|
|
endUpdate();
|
|
}
|
|
|
|
QString SynEditStringList::getString(int Index)
|
|
{
|
|
if (Index<0 || Index>=mList.count()) {
|
|
return QString();
|
|
}
|
|
return mList[Index]->fString;
|
|
}
|
|
|
|
int SynEditStringList::count()
|
|
{
|
|
return mList.count();
|
|
}
|
|
|
|
void *SynEditStringList::getObject(int Index)
|
|
{
|
|
if (Index<0 || Index>=mList.count()) {
|
|
return nullptr;
|
|
}
|
|
return mList[Index]->fObject;
|
|
}
|
|
|
|
QString SynEditStringList::text()
|
|
{
|
|
return getTextStr();
|
|
}
|
|
|
|
void SynEditStringList::setText(const QString &text)
|
|
{
|
|
PutTextStr(text);
|
|
}
|
|
|
|
void SynEditStringList::setContents(const QStringList &text)
|
|
{
|
|
beginUpdate();
|
|
auto action = finally([this]{
|
|
endUpdate();
|
|
});
|
|
clear();
|
|
if (text.count() > 0) {
|
|
mIndexOfLongestLine = -1;
|
|
int FirstAdded = mList.count();
|
|
|
|
foreach (const QString& s,text) {
|
|
addItem(s);
|
|
}
|
|
emit inserted(FirstAdded,text.count());
|
|
}
|
|
}
|
|
|
|
QStringList SynEditStringList::contents()
|
|
{
|
|
QStringList Result;
|
|
for (PSynEditStringRec& line:mList) {
|
|
Result.append(line->fString);
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
void SynEditStringList::beginUpdate()
|
|
{
|
|
if (mUpdateCount == 0) {
|
|
SetUpdateState(true);
|
|
}
|
|
mUpdateCount++;
|
|
}
|
|
|
|
void SynEditStringList::endUpdate()
|
|
{
|
|
mUpdateCount--;
|
|
if (mUpdateCount == 0) {
|
|
SetUpdateState(false);
|
|
}
|
|
}
|
|
|
|
|
|
int SynEditStringList::add(const QString &s)
|
|
{
|
|
beginUpdate();
|
|
int Result = mList.count();
|
|
InsertItem(Result, s);
|
|
emit inserted(Result,1);
|
|
endUpdate();
|
|
return Result;
|
|
}
|
|
|
|
void SynEditStringList::addStrings(const QStringList &Strings)
|
|
{
|
|
if (Strings.count() > 0) {
|
|
mIndexOfLongestLine = -1;
|
|
beginUpdate();
|
|
auto action = finally([this]{
|
|
endUpdate();
|
|
});
|
|
int FirstAdded = mList.count();
|
|
|
|
for (const QString& s:Strings) {
|
|
addItem(s);
|
|
}
|
|
emit inserted(FirstAdded,Strings.count());
|
|
}
|
|
}
|
|
|
|
int SynEditStringList::getTextLength()
|
|
{
|
|
int Result = 0;
|
|
foreach (const PSynEditStringRec& line, mList ) {
|
|
Result += line->fString.length();
|
|
if (mFileEndingType == FileEndingType::Windows) {
|
|
Result += 2;
|
|
} else {
|
|
Result += 1;
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
void SynEditStringList::clear()
|
|
{
|
|
if (!mList.isEmpty()) {
|
|
beginUpdate();
|
|
int oldCount = mList.count();
|
|
mIndexOfLongestLine = -1;
|
|
mList.clear();
|
|
emit deleted(0,oldCount);
|
|
endUpdate();
|
|
}
|
|
}
|
|
|
|
void SynEditStringList::deleteLines(int Index, int NumLines)
|
|
{
|
|
if (NumLines<=0)
|
|
return;
|
|
if ((Index < 0) || (Index >= mList.count())) {
|
|
ListIndexOutOfBounds(Index);
|
|
}
|
|
beginUpdate();
|
|
auto action = finally([this]{
|
|
endUpdate();
|
|
});
|
|
if (mIndexOfLongestLine>=Index && (mIndexOfLongestLine <Index+NumLines)) {
|
|
mIndexOfLongestLine = - 1;
|
|
}
|
|
int LinesAfter = mList.count() - (Index + NumLines);
|
|
if (LinesAfter < 0) {
|
|
NumLines = mList.count() - Index;
|
|
}
|
|
mList.remove(Index,NumLines);
|
|
emit deleted(Index,NumLines);
|
|
}
|
|
|
|
void SynEditStringList::Exchange(int Index1, int Index2)
|
|
{
|
|
if ((Index1 < 0) || (Index1 >= mList.count())) {
|
|
ListIndexOutOfBounds(Index1);
|
|
}
|
|
if ((Index2 < 0) || (Index2 >= mList.count())) {
|
|
ListIndexOutOfBounds(Index2);
|
|
}
|
|
beginUpdate();
|
|
mList.swapItemsAt(Index1,Index2);
|
|
if (mIndexOfLongestLine == Index1) {
|
|
mIndexOfLongestLine = Index2;
|
|
} else if (mIndexOfLongestLine == Index2) {
|
|
mIndexOfLongestLine = Index1;
|
|
}
|
|
endUpdate();
|
|
}
|
|
|
|
void SynEditStringList::Insert(int Index, const QString &s)
|
|
{
|
|
if ((Index < 0) || (Index > mList.count())) {
|
|
ListIndexOutOfBounds(Index);
|
|
}
|
|
beginUpdate();
|
|
InsertItem(Index, s);
|
|
emit inserted(Index,1);
|
|
endUpdate();
|
|
}
|
|
|
|
void SynEditStringList::deleteAt(int Index)
|
|
{
|
|
if ((Index < 0) || (Index >= mList.count())) {
|
|
ListIndexOutOfBounds(Index);
|
|
}
|
|
beginUpdate();
|
|
if (mIndexOfLongestLine == Index)
|
|
mIndexOfLongestLine = -1;
|
|
mList.removeAt(Index);
|
|
emit deleted(Index,1);
|
|
endUpdate();
|
|
}
|
|
|
|
QString SynEditStringList::getTextStr()
|
|
{
|
|
QString result;
|
|
for (int i=0;i<mList.count()-1;i++) {
|
|
const PSynEditStringRec& line = mList[i];
|
|
result.append(line->fString);
|
|
result.append(lineBreak());
|
|
}
|
|
if (mList.length()>0) {
|
|
result.append(mList.back()->fString);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void SynEditStringList::putString(int Index, const QString &s) {
|
|
if (Index == mList.count()) {
|
|
add(s);
|
|
} else {
|
|
if (Index<0 || Index>=mList.count()) {
|
|
ListIndexOutOfBounds(Index);
|
|
}
|
|
beginUpdate();
|
|
mIndexOfLongestLine = -1;
|
|
mList[Index]->fString = s;
|
|
mList[Index]->fColumns = -1;
|
|
emit putted(Index,1);
|
|
endUpdate();
|
|
}
|
|
}
|
|
|
|
void SynEditStringList::putObject(int Index, void *AObject)
|
|
{
|
|
if (Index<0 || Index>=mList.count()) {
|
|
ListIndexOutOfBounds(Index);
|
|
}
|
|
beginUpdate();
|
|
mList[Index]->fObject = AObject;
|
|
endUpdate();
|
|
}
|
|
|
|
void SynEditStringList::SetUpdateState(bool Updating)
|
|
{
|
|
if (Updating)
|
|
emit changing();
|
|
else
|
|
emit changed();
|
|
}
|
|
|
|
int SynEditStringList::calculateLineColumns(int Index)
|
|
{
|
|
PSynEditStringRec line = mList[Index];
|
|
|
|
line->fColumns = mEdit->stringColumns(line->fString,0);
|
|
return line->fColumns;
|
|
}
|
|
|
|
void SynEditStringList::InsertLines(int Index, int NumLines)
|
|
{
|
|
if (Index<0 || Index>mList.count()) {
|
|
ListIndexOutOfBounds(Index);
|
|
}
|
|
if (NumLines<=0)
|
|
return;
|
|
beginUpdate();
|
|
auto action = finally([this]{
|
|
endUpdate();
|
|
});
|
|
PSynEditStringRec line;
|
|
mList.insert(Index,NumLines,line);
|
|
for (int i=Index;i<Index+NumLines;i++) {
|
|
line = std::make_shared<SynEditStringRec>();
|
|
mList[i]=line;
|
|
}
|
|
emit inserted(Index,NumLines);
|
|
}
|
|
|
|
void SynEditStringList::InsertStrings(int Index, const QStringList &NewStrings)
|
|
{
|
|
if (Index<0 || Index>mList.count()) {
|
|
ListIndexOutOfBounds(Index);
|
|
}
|
|
if (NewStrings.isEmpty())
|
|
return;
|
|
beginUpdate();
|
|
auto action = finally([this]{
|
|
endUpdate();
|
|
});
|
|
PSynEditStringRec line;
|
|
mList.insert(Index,NewStrings.length(),line);
|
|
for (int i=0;i<NewStrings.length();i++) {
|
|
line = std::make_shared<SynEditStringRec>();
|
|
line->fString = NewStrings[i];
|
|
mList[i+Index]=line;
|
|
}
|
|
emit inserted(Index,NewStrings.length());
|
|
}
|
|
|
|
void SynEditStringList::InsertText(int Index, const QString &NewText)
|
|
{
|
|
if (Index<0 || Index>=mList.count()) {
|
|
ListIndexOutOfBounds(Index);
|
|
}
|
|
if (NewText.isEmpty())
|
|
return;
|
|
QStringList lines = TextToLines(NewText);
|
|
InsertStrings(Index,lines);
|
|
}
|
|
|
|
void SynEditStringList::LoadFromFile(QFile &file, const QByteArray& encoding, QByteArray& realEncoding)
|
|
{
|
|
if (!file.open(QFile::ReadOnly))
|
|
throw FileError(tr("Can't open file '%1' for read!").arg(file.fileName()));
|
|
beginUpdate();
|
|
auto action = finally([this]{
|
|
endUpdate();
|
|
});
|
|
|
|
//test for utf8 / utf 8 bom
|
|
if (encoding == ENCODING_AUTO_DETECT) {
|
|
if (file.atEnd()) {
|
|
realEncoding = ENCODING_ASCII;
|
|
return;
|
|
}
|
|
QByteArray line = file.readLine();
|
|
QTextCodec* codec;
|
|
QTextCodec::ConverterState state;
|
|
bool needReread = false;
|
|
bool allAscii = true;
|
|
//test for BOM
|
|
if ((line.length()>=3) && ((unsigned char)line[0]==0xEF) && ((unsigned char)line[1]==0xBB) && ((unsigned char)line[2]==0xBF) ) {
|
|
realEncoding = ENCODING_UTF8_BOM;
|
|
line = line.mid(3);
|
|
codec = QTextCodec::codecForName(ENCODING_UTF8);
|
|
} else {
|
|
realEncoding = ENCODING_UTF8;
|
|
codec = QTextCodec::codecForName(ENCODING_UTF8);
|
|
}
|
|
if (line.endsWith("\r\n")) {
|
|
mFileEndingType = FileEndingType::Windows;
|
|
} else if (line.endsWith("\n")) {
|
|
mFileEndingType = FileEndingType::Linux;
|
|
} else if (line.endsWith("\r")) {
|
|
mFileEndingType = FileEndingType::Mac;
|
|
}
|
|
clear();
|
|
do {
|
|
if (allAscii) {
|
|
allAscii = isTextAllAscii(line);
|
|
}
|
|
if (allAscii) {
|
|
addItem(TrimRight(QString::fromLatin1(line)));
|
|
} else {
|
|
QString newLine = codec->toUnicode(line.constData(),line.length(),&state);
|
|
if (state.invalidChars>0) {
|
|
needReread = true;
|
|
break;
|
|
}
|
|
addItem(TrimRight(newLine));
|
|
}
|
|
if (file.atEnd()){
|
|
break;
|
|
}
|
|
line = file.readLine();
|
|
} while (true);
|
|
if (!needReread) {
|
|
if (allAscii)
|
|
realEncoding = ENCODING_ASCII;
|
|
return;
|
|
}
|
|
realEncoding = ENCODING_SYSTEM_DEFAULT;
|
|
} else {
|
|
realEncoding = encoding;
|
|
}
|
|
|
|
if (realEncoding == ENCODING_SYSTEM_DEFAULT) {
|
|
realEncoding = QTextCodec::codecForLocale()->name();
|
|
}
|
|
file.reset();
|
|
QTextStream textStream(&file);
|
|
if (realEncoding == ENCODING_UTF8_BOM) {
|
|
textStream.setAutoDetectUnicode(true);
|
|
textStream.setCodec(ENCODING_UTF8);
|
|
} else {
|
|
textStream.setAutoDetectUnicode(false);
|
|
textStream.setCodec(realEncoding);
|
|
}
|
|
QString line;
|
|
clear();
|
|
while (textStream.readLineInto(&line)) {
|
|
addItem(TrimRight(line));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void SynEditStringList::SaveToFile(QFile &file, const QByteArray& encoding, QByteArray& realEncoding)
|
|
{
|
|
if (!file.open(QFile::WriteOnly | QFile::Truncate))
|
|
throw FileError(tr("Can't open file '%1' for save!").arg(file.fileName()));
|
|
if (mList.isEmpty())
|
|
return;
|
|
bool allAscii = true;
|
|
|
|
QTextCodec* codec;
|
|
realEncoding = encoding;
|
|
if (realEncoding == ENCODING_UTF8_BOM) {
|
|
codec = QTextCodec::codecForName(ENCODING_UTF8);
|
|
file.putChar(0xEF);
|
|
file.putChar(0xBB);
|
|
file.putChar(0xBF);
|
|
} else if (realEncoding == ENCODING_SYSTEM_DEFAULT) {
|
|
codec = QTextCodec::codecForLocale();
|
|
} else if (realEncoding == ENCODING_AUTO_DETECT) {
|
|
codec = QTextCodec::codecForLocale();
|
|
} else {
|
|
codec = QTextCodec::codecForName(realEncoding);
|
|
}
|
|
for (PSynEditStringRec& line:mList) {
|
|
if (allAscii) {
|
|
allAscii = isTextAllAscii(line->fString);
|
|
}
|
|
if (!allAscii) {
|
|
file.write(codec->fromUnicode(line->fString));
|
|
} else {
|
|
file.write(line->fString.toLatin1());
|
|
}
|
|
file.write(lineBreak().toLatin1());
|
|
}
|
|
if (encoding == ENCODING_AUTO_DETECT) {
|
|
if (allAscii)
|
|
realEncoding = ENCODING_ASCII;
|
|
else
|
|
realEncoding = codec->name();
|
|
}
|
|
}
|
|
|
|
void SynEditStringList::PutTextStr(const QString &text)
|
|
{
|
|
beginUpdate();
|
|
auto action = finally([this]{
|
|
endUpdate();
|
|
});
|
|
clear();
|
|
int pos = 0;
|
|
int start;
|
|
while (pos < text.length()) {
|
|
start = pos;
|
|
while (pos<text.length()) {
|
|
if (text[pos] == '\r' || text[pos] == '\n') {
|
|
break;
|
|
}
|
|
pos++;
|
|
}
|
|
add(text.mid(start,pos-start));
|
|
if (pos>=text.length())
|
|
break;
|
|
if (text[pos] == '\r')
|
|
pos++;
|
|
if (text[pos] == '\n')
|
|
pos++;
|
|
}
|
|
}
|
|
|
|
FileEndingType SynEditStringList::getFileEndingType() const
|
|
{
|
|
return mFileEndingType;
|
|
}
|
|
|
|
void SynEditStringList::setFileEndingType(const FileEndingType &fileEndingType)
|
|
{
|
|
mFileEndingType = fileEndingType;
|
|
}
|
|
|
|
bool SynEditStringList::empty()
|
|
{
|
|
return count()==0;
|
|
}
|
|
|
|
void SynEditStringList::resetColumns()
|
|
{
|
|
mIndexOfLongestLine = -1;
|
|
if (mList.count() > 0 ) {
|
|
for (int i=0;i<mList.size();i++) {
|
|
mList[i]->fColumns = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SynEditStringList::invalidAllLineColumns()
|
|
{
|
|
mIndexOfLongestLine = -1;
|
|
for (PSynEditStringRec& line:mList) {
|
|
line->fColumns = -1;
|
|
}
|
|
}
|
|
|
|
SynEditStringRec::SynEditStringRec():
|
|
fString(),
|
|
fObject(nullptr),
|
|
fRange{0,0},
|
|
fColumns(-1),
|
|
fParenthesisLevel(0),
|
|
fBracketLevel(0),
|
|
fBraceLevel(0)
|
|
{
|
|
}
|
|
|
|
|
|
SynEditUndoList::SynEditUndoList():QObject()
|
|
{
|
|
mMaxUndoActions = 1024;
|
|
mNextChangeNumber = 1;
|
|
mInsideRedo = false;
|
|
|
|
mBlockChangeNumber=0;
|
|
mBlockCount=0;
|
|
mFullUndoImposible=false;
|
|
mLockCount = 0;
|
|
mInitialChangeNumber = 0;
|
|
}
|
|
|
|
void SynEditUndoList::AddChange(SynChangeReason AReason, const BufferCoord &AStart,
|
|
const BufferCoord &AEnd, const QString &ChangeText,
|
|
SynSelectionMode SelMode)
|
|
{
|
|
if (mLockCount != 0)
|
|
return;
|
|
int changeNumber;
|
|
if (mBlockChangeNumber != 0) {
|
|
changeNumber = mBlockChangeNumber;
|
|
} else {
|
|
changeNumber = mNextChangeNumber;
|
|
if (mBlockCount == 0) {
|
|
mNextChangeNumber++;
|
|
if (mNextChangeNumber == 0) {
|
|
mNextChangeNumber++;
|
|
}
|
|
}
|
|
}
|
|
PSynEditUndoItem NewItem = std::make_shared<SynEditUndoItem>(AReason,
|
|
SelMode,AStart,AEnd,ChangeText,
|
|
changeNumber);
|
|
PushItem(NewItem);
|
|
}
|
|
|
|
void SynEditUndoList::AddGroupBreak()
|
|
{
|
|
//Add the GroupBreak even if ItemCount = 0. Since items are stored in
|
|
//reverse order in TCustomSynEdit.fRedoList, a GroupBreak could be lost.
|
|
if (LastChangeReason() != SynChangeReason::crGroupBreak) {
|
|
AddChange(SynChangeReason::crGroupBreak, {0,0}, {0,0}, "", SynSelectionMode::smNormal);
|
|
}
|
|
}
|
|
|
|
void SynEditUndoList::BeginBlock()
|
|
{
|
|
mBlockCount++;
|
|
mBlockChangeNumber = mNextChangeNumber;
|
|
}
|
|
|
|
void SynEditUndoList::Clear()
|
|
{
|
|
mItems.clear();
|
|
mFullUndoImposible = false;
|
|
}
|
|
|
|
void SynEditUndoList::DeleteItem(int index)
|
|
{
|
|
if (index <0 || index>=mItems.count()) {
|
|
ListIndexOutOfBounds(index);
|
|
}
|
|
mItems.removeAt(index);
|
|
}
|
|
|
|
void SynEditUndoList::EndBlock()
|
|
{
|
|
if (mBlockCount > 0) {
|
|
mBlockCount--;
|
|
if (mBlockCount == 0) {
|
|
int iBlockID = mBlockChangeNumber;
|
|
mBlockChangeNumber = 0;
|
|
mNextChangeNumber++;
|
|
if (mNextChangeNumber == 0)
|
|
mNextChangeNumber++;
|
|
if (mItems.count() > 0 && PeekItem()->changeNumber() == iBlockID)
|
|
emit addedUndo();
|
|
}
|
|
}
|
|
}
|
|
|
|
SynChangeReason SynEditUndoList::LastChangeReason()
|
|
{
|
|
if (mItems.count() == 0)
|
|
return SynChangeReason::crNothing;
|
|
else
|
|
return mItems.last()->changeReason();
|
|
}
|
|
|
|
void SynEditUndoList::Lock()
|
|
{
|
|
mLockCount++;
|
|
}
|
|
|
|
PSynEditUndoItem SynEditUndoList::PeekItem()
|
|
{
|
|
if (mItems.count() == 0)
|
|
return PSynEditUndoItem();
|
|
else
|
|
return mItems.last();
|
|
}
|
|
|
|
PSynEditUndoItem SynEditUndoList::PopItem()
|
|
{
|
|
if (mItems.count() == 0)
|
|
return PSynEditUndoItem();
|
|
else {
|
|
PSynEditUndoItem item = mItems.last();
|
|
mItems.removeLast();
|
|
return item;
|
|
}
|
|
}
|
|
|
|
void SynEditUndoList::PushItem(PSynEditUndoItem Item)
|
|
{
|
|
if (!Item)
|
|
return;
|
|
mItems.append(Item);
|
|
EnsureMaxEntries();
|
|
if (Item->changeReason()!= SynChangeReason::crGroupBreak)
|
|
emit addedUndo();
|
|
}
|
|
|
|
void SynEditUndoList::Unlock()
|
|
{
|
|
if (mLockCount > 0)
|
|
mLockCount--;
|
|
}
|
|
|
|
bool SynEditUndoList::CanUndo()
|
|
{
|
|
return mItems.count()>0;
|
|
}
|
|
|
|
int SynEditUndoList::ItemCount()
|
|
{
|
|
return mItems.count();
|
|
}
|
|
|
|
int SynEditUndoList::maxUndoActions() const
|
|
{
|
|
return mMaxUndoActions;
|
|
}
|
|
|
|
void SynEditUndoList::setMaxUndoActions(int maxUndoActions)
|
|
{
|
|
mMaxUndoActions = maxUndoActions;
|
|
}
|
|
|
|
bool SynEditUndoList::initialState()
|
|
{
|
|
if (ItemCount() == 0) {
|
|
return mInitialChangeNumber == 0;
|
|
} else {
|
|
return PeekItem()->changeNumber() == mInitialChangeNumber;
|
|
}
|
|
}
|
|
|
|
PSynEditUndoItem SynEditUndoList::item(int index)
|
|
{
|
|
if (index <0 || index>=mItems.count()) {
|
|
ListIndexOutOfBounds(index);
|
|
}
|
|
return mItems[index];
|
|
}
|
|
|
|
void SynEditUndoList::setInitialState(const bool Value)
|
|
{
|
|
if (Value) {
|
|
if (ItemCount() == 0)
|
|
mInitialChangeNumber = 0;
|
|
else
|
|
mInitialChangeNumber = PeekItem()->changeNumber();
|
|
} else if (ItemCount() == 0) {
|
|
if (mInitialChangeNumber == 0) {
|
|
mInitialChangeNumber = -1;
|
|
}
|
|
} else if (PeekItem()->changeNumber() == mInitialChangeNumber) {
|
|
mInitialChangeNumber = -1;
|
|
}
|
|
}
|
|
|
|
void SynEditUndoList::setItem(int index, PSynEditUndoItem Value)
|
|
{
|
|
if (index <0 || index>=mItems.count()) {
|
|
ListIndexOutOfBounds(index);
|
|
}
|
|
mItems[index]=Value;
|
|
}
|
|
|
|
int SynEditUndoList::blockChangeNumber() const
|
|
{
|
|
return mBlockChangeNumber;
|
|
}
|
|
|
|
void SynEditUndoList::setBlockChangeNumber(int blockChangeNumber)
|
|
{
|
|
mBlockChangeNumber = blockChangeNumber;
|
|
}
|
|
|
|
int SynEditUndoList::blockCount() const
|
|
{
|
|
return mBlockCount;
|
|
}
|
|
|
|
bool SynEditUndoList::insideRedo() const
|
|
{
|
|
return mInsideRedo;
|
|
}
|
|
|
|
void SynEditUndoList::setInsideRedo(bool insideRedo)
|
|
{
|
|
mInsideRedo = insideRedo;
|
|
}
|
|
|
|
bool SynEditUndoList::fullUndoImposible() const
|
|
{
|
|
return mFullUndoImposible;
|
|
}
|
|
|
|
void SynEditUndoList::EnsureMaxEntries()
|
|
{
|
|
if (mItems.count() > mMaxUndoActions){
|
|
mFullUndoImposible = true;
|
|
while (mItems.count() > mMaxUndoActions) {
|
|
mItems.removeFirst();
|
|
}
|
|
}
|
|
}
|
|
|
|
SynSelectionMode SynEditUndoItem::changeSelMode() const
|
|
{
|
|
return mChangeSelMode;
|
|
}
|
|
|
|
BufferCoord SynEditUndoItem::changeStartPos() const
|
|
{
|
|
return mChangeStartPos;
|
|
}
|
|
|
|
BufferCoord SynEditUndoItem::changeEndPos() const
|
|
{
|
|
return mChangeEndPos;
|
|
}
|
|
|
|
QString SynEditUndoItem::changeStr() const
|
|
{
|
|
return mChangeStr;
|
|
}
|
|
|
|
int SynEditUndoItem::changeNumber() const
|
|
{
|
|
return mChangeNumber;
|
|
}
|
|
|
|
SynEditUndoItem::SynEditUndoItem(SynChangeReason reason, SynSelectionMode selMode,
|
|
BufferCoord startPos, BufferCoord endPos,
|
|
const QString &str, int number)
|
|
{
|
|
mChangeReason = reason;
|
|
mChangeSelMode = selMode;
|
|
mChangeStartPos = startPos;
|
|
mChangeEndPos = endPos;
|
|
mChangeStr = str;
|
|
mChangeNumber = number;
|
|
}
|
|
|
|
SynChangeReason SynEditUndoItem::changeReason() const
|
|
{
|
|
return mChangeReason;
|
|
}
|