diff --git a/RedPandaIDE/RedPandaIDE.pro b/RedPandaIDE/RedPandaIDE.pro index 5177cf75..6ed93a3a 100644 --- a/RedPandaIDE/RedPandaIDE.pro +++ b/RedPandaIDE/RedPandaIDE.pro @@ -19,10 +19,12 @@ SOURCES += \ mainwindow.cpp \ qsynedit/CodeFolding.cpp \ qsynedit/Constants.cpp \ + qsynedit/KeyStrokes.cpp \ qsynedit/MiscClasses.cpp \ qsynedit/MiscProcs.cpp \ qsynedit/SynEdit.cpp \ qsynedit/TextBuffer.cpp \ + qsynedit/TextDrawer.cpp \ qsynedit/highlighter/base.cpp \ qsynedit/highlighter/composition.cpp \ qsynedit/highlighter/cpp.cpp \ @@ -45,10 +47,12 @@ HEADERS += \ mainwindow.h \ qsynedit/CodeFolding.h \ qsynedit/Constants.h \ + qsynedit/KeyStrokes.h \ qsynedit/MiscClasses.h \ qsynedit/MiscProcs.h \ qsynedit/SynEdit.h \ qsynedit/TextBuffer.h \ + qsynedit/TextDrawer.h \ qsynedit/Types.h \ qsynedit/highlighter/base.h \ qsynedit/highlighter/composition.h \ diff --git a/RedPandaIDE/qsynedit/CodeFolding.cpp b/RedPandaIDE/qsynedit/CodeFolding.cpp index eb40bb59..c34b2b93 100644 --- a/RedPandaIDE/qsynedit/CodeFolding.cpp +++ b/RedPandaIDE/qsynedit/CodeFolding.cpp @@ -1,13 +1,13 @@ #include "CodeFolding.h" -int QSynFoldRegions::count() +int SynEditFoldRegions::count() { return fRegions.size(); } -int QSynFoldRegions::add(bool addEnding, const QChar &openSymbol, const QChar &closeSymbol, const QString &highlight) +int SynEditFoldRegions::add(bool addEnding, const QChar &openSymbol, const QChar &closeSymbol, const QString &highlight) { - PSynFoldRegion region = std::make_shared(); + PSynEditFoldRegion region = std::make_shared(); region->addEnding = addEnding; region->openSymbol = openSymbol; region->closeSymbol = closeSymbol; @@ -15,12 +15,12 @@ int QSynFoldRegions::add(bool addEnding, const QChar &openSymbol, const QChar &c fRegions.push_back(region); } -PSynFoldRegion QSynFoldRegions::get(int index) +PSynEditFoldRegion SynEditFoldRegions::get(int index) { return fRegions.at(index); } -QSynCodeFolding::QSynCodeFolding(): +SynEditCodeFolding::SynEditCodeFolding(): indentGuides(true), showCollapsedLine(true), collapsedLineColor(QColor("black")), @@ -31,7 +31,7 @@ QSynCodeFolding::QSynCodeFolding(): } -bool QSynEditFoldRange::parentCollapsed() +bool SynEditFoldRange::parentCollapsed() { PSynEditFoldRange parentFold = parent; // Find first parent that is collapsed @@ -44,13 +44,13 @@ bool QSynEditFoldRange::parentCollapsed() return false; } -void QSynEditFoldRange::move(int count) +void SynEditFoldRange::move(int count) { fromLine += count; toLine += count; } -QSynEditFoldRange::QSynEditFoldRange(PSynEditFoldRange aParent, PSynEditFoldRanges aAllFold, int aFromLine, PSynFoldRegion aFoldRegion, int aToLine): +SynEditFoldRange::SynEditFoldRange(PSynEditFoldRange aParent, PSynEditFoldRanges aAllFold, int aFromLine, PSynEditFoldRegion aFoldRegion, int aToLine): fromLine(aFromLine), toLine(aToLine), linesCollapsed(0), @@ -64,33 +64,33 @@ QSynEditFoldRange::QSynEditFoldRange(PSynEditFoldRange aParent, PSynEditFoldRang } -PSynEditFoldRange QSynEditFoldRanges::foldRange(int index) +PSynEditFoldRange SynEditFoldRanges::foldRange(int index) { return ranges[index]; } -int QSynEditFoldRanges::count() +int SynEditFoldRanges::count() { return ranges.size(); } -QSynEditFoldRanges::QSynEditFoldRanges() +SynEditFoldRanges::SynEditFoldRanges() { } -PSynEditFoldRange QSynEditFoldRanges::addByParts(PSynEditFoldRange aParent, PSynEditFoldRanges aAllFold, int aFromLine, PSynFoldRegion aFoldRegion, int aToLine) +PSynEditFoldRange SynEditFoldRanges::addByParts(PSynEditFoldRange aParent, PSynEditFoldRanges aAllFold, int aFromLine, PSynEditFoldRegion aFoldRegion, int aToLine) { - PSynEditFoldRange range=std::make_shared(aParent,aAllFold, aFromLine,aFoldRegion,aToLine); + PSynEditFoldRange range=std::make_shared(aParent,aAllFold, aFromLine,aFoldRegion,aToLine); return range; } -int QSynEditFoldRanges::remove(int index) +int SynEditFoldRanges::remove(int index) { ranges.erase(ranges.begin()+index); } -void QSynEditFoldRanges::addObject(PSynEditFoldRange foldRange) +void SynEditFoldRanges::addObject(PSynEditFoldRange foldRange) { ranges.push_back(foldRange); } diff --git a/RedPandaIDE/qsynedit/CodeFolding.h b/RedPandaIDE/qsynedit/CodeFolding.h index 2e4f69a3..de1ef13d 100644 --- a/RedPandaIDE/qsynedit/CodeFolding.h +++ b/RedPandaIDE/qsynedit/CodeFolding.h @@ -3,73 +3,74 @@ #include #include #include +#include -struct QSynFoldRegion; -typedef std::shared_ptr PSynFoldRegion; +struct SynEditFoldRegion; +typedef std::shared_ptr PSynEditFoldRegion; -class QSynFoldRegions { +class SynEditFoldRegions { private: - std::vector fRegions; + std::vector fRegions; public: int count(); int add(bool addEnding, const QChar& openSymbol, const QChar& closeSymbol, const QString& highlight); - PSynFoldRegion get(int index); + PSynEditFoldRegion get(int index); }; -typedef std::shared_ptr PSynFoldRegions; +typedef std::shared_ptr PSynFoldRegions; -struct QSynFoldRegion { +struct SynEditFoldRegion { bool addEnding; - QSynFoldRegions subFoldRegions; + SynEditFoldRegions subFoldRegions; QChar openSymbol; QChar closeSymbol; QString highlight; }; -struct QSynCodeFolding { +struct SynEditCodeFolding { bool indentGuides; bool showCollapsedLine; QColor collapsedLineColor; QColor folderBarLinesColor; QColor indentGuidesColor; - QSynFoldRegions foldRegions; - QSynCodeFolding(); + SynEditFoldRegions foldRegions; + SynEditCodeFolding(); }; -class QSynEditFoldRange; -typedef std::shared_ptr PSynEditFoldRange; -class QSynEditFoldRanges; -typedef std::shared_ptr PSynEditFoldRanges; +class SynEditFoldRange; +typedef std::shared_ptr PSynEditFoldRange; +class SynEditFoldRanges; +typedef std::shared_ptr PSynEditFoldRanges; -class QSynEditFoldRanges{ +class SynEditFoldRanges{ public: - std::vector ranges; + QVector ranges; PSynEditFoldRange foldRange(int index); int count(); - QSynEditFoldRanges(); + SynEditFoldRanges(); PSynEditFoldRange addByParts(PSynEditFoldRange aParent, PSynEditFoldRanges aAllFold, - int aFromLine, PSynFoldRegion aFoldRegion, int aToLine); + int aFromLine, PSynEditFoldRegion aFoldRegion, int aToLine); int remove(int index); void addObject(PSynEditFoldRange foldRange); }; // A single fold -class QSynEditFoldRange { +class SynEditFoldRange { public: int fromLine; // Beginning line int toLine; // End line int linesCollapsed; // Number of collapsed lines - QSynEditFoldRanges subFoldRanges; // Sub fold ranges + SynEditFoldRanges subFoldRanges; // Sub fold ranges bool collapsed; // Is collapsed? PSynEditFoldRanges allFoldRanges;// TAllFoldRanges pointer - PSynFoldRegion foldRegion; // FoldRegion + PSynEditFoldRegion foldRegion; // FoldRegion int hintMarkLeft; PSynEditFoldRange parent; bool parentCollapsed(); void move(int count); - explicit QSynEditFoldRange(PSynEditFoldRange aParent, PSynEditFoldRanges aAllFold, - int aFromLine, PSynFoldRegion aFoldRegion, int aToLine); + explicit SynEditFoldRange(PSynEditFoldRange aParent, PSynEditFoldRanges aAllFold, + int aFromLine, PSynEditFoldRegion aFoldRegion, int aToLine); }; #endif // CODEFOLDING_H diff --git a/RedPandaIDE/qsynedit/KeyStrokes.cpp b/RedPandaIDE/qsynedit/KeyStrokes.cpp new file mode 100644 index 00000000..15b7665d --- /dev/null +++ b/RedPandaIDE/qsynedit/KeyStrokes.cpp @@ -0,0 +1,150 @@ +#include "KeyStrokes.h" + +SynEditKeyStroke::SynEditKeyStroke() +{ + mKey = 0; + mKeyModifiers = Qt::NoModifier; + mKey2 = 0; + mKeyModifiers2 = Qt::NoModifier; + mCommand = SynEditorCommand::ecNone; +} + +QKeySequence SynEditKeyStroke::keySequence() const +{ + if (mKey2 == 0) { + return QKeySequence(mKey + mKeyModifiers); + } else { + return QKeySequence(mKey + mKeyModifiers, mKey2+mKeyModifiers2); + } +} + +void SynEditKeyStroke::setKeySequence(QKeySequence &keySequence) +{ + if (keySequence.isEmpty()<=0) + return; + decodeKey(keySequence[0],mKey,mKeyModifiers); + if (keySequence.count()>1) { + decodeKey(keySequence[1],mKey2,mKeyModifiers2); + } else { + mKey2=0; + mKeyModifiers2=Qt::NoModifier; + } +} + +int SynEditKeyStroke::key() const +{ + return mKey; +} + +void SynEditKeyStroke::setKey(int key) +{ + mKey = key; +} + +Qt::KeyboardModifiers SynEditKeyStroke::keyModifiers() const +{ + return mKeyModifiers; +} + +void SynEditKeyStroke::setKeyModifiers(const Qt::KeyboardModifiers &keyModifiers) +{ + mKeyModifiers = keyModifiers; +} + +int SynEditKeyStroke::key2() const +{ + return mKey2; +} + +void SynEditKeyStroke::setKey2(int key2) +{ + mKey2 = key2; +} + +Qt::KeyboardModifiers SynEditKeyStroke::keyModifiers2() const +{ + return mKeyModifiers2; +} + +void SynEditKeyStroke::setKeyModifiers2(const Qt::KeyboardModifiers &keyModifiers2) +{ + mKeyModifiers2 = keyModifiers2; +} + +SynEditorCommand SynEditKeyStroke::command() const +{ + return mCommand; +} + +void SynEditKeyStroke::setCommand(const SynEditorCommand &command) +{ + mCommand = command; +} + +SynKeyError::SynKeyError(const QString &reason):BaseError(reason) +{ + +} + +PSynEditKeyStroke SynEditKeyStrokes::add(int key, Qt::KeyboardModifiers modifiers, SynEditorCommand command) +{ + PSynEditKeyStroke keyStroke = std::make_shared(); + keyStroke->setKey(key); + keyStroke->setKeyModifiers(modifiers); + keyStroke->setCommand(command); +} + +PSynEditKeyStroke SynEditKeyStrokes::findCommand(SynEditorCommand command) +{ + for (PSynEditKeyStroke& keyStroke:mList) { + if (keyStroke->command() == command) + return keyStroke; + } + return PSynEditKeyStroke(); +} + +PSynEditKeyStroke SynEditKeyStrokes::findKeycode(int key, Qt::KeyboardModifiers modifiers) +{ + for (PSynEditKeyStroke& keyStroke:mList) { + if (keyStroke->key() == key && keyStroke->keyModifiers()==modifiers && keyStroke->key2()==0) + return keyStroke; + } + return PSynEditKeyStroke(); +} + +PSynEditKeyStroke SynEditKeyStrokes::findKeycode2(int key, Qt::KeyboardModifiers modifiers, + int key2, Qt::KeyboardModifiers modifiers2) +{ + for (PSynEditKeyStroke& keyStroke:mList) { + if (keyStroke->key() == key && keyStroke->keyModifiers()==modifiers && keyStroke->key2()==key2 + && keyStroke->keyModifiers2() ==modifiers2) + return keyStroke; + } + return PSynEditKeyStroke(); +} + +PSynEditKeyStroke SynEditKeyStrokes::findKeySequence(const QKeySequence &keySeq) +{ + switch (keySeq.count()) { + case 1: { + int key; + Qt::KeyboardModifiers modifiers; + decodeKey(keySeq[0],key,modifiers); + return findKeycode(key,modifiers); + } + case 2: + case 3: + case 4: + { + int key; + Qt::KeyboardModifiers modifiers; + int key2; + Qt::KeyboardModifiers modifiers2; + decodeKey(keySeq[0],key,modifiers); + decodeKey(keySeq[1],key2,modifiers2); + return findKeycode2(key,modifiers,key2,modifiers2); + } + default: + return PSynEditKeyStroke(); + } +} diff --git a/RedPandaIDE/qsynedit/KeyStrokes.h b/RedPandaIDE/qsynedit/KeyStrokes.h new file mode 100644 index 00000000..3d9abc5b --- /dev/null +++ b/RedPandaIDE/qsynedit/KeyStrokes.h @@ -0,0 +1,223 @@ +#ifndef SYNEDITKEYSTROKE_H +#define SYNEDITKEYSTROKE_H + +#include "../utils.h" +#include +#include +#include +//**************************************************************************** +// NOTE! If you add an editor command, you must also update the +// EditorCommandStrs constant array in implementation section below, or the +// command will not show up in the IDE. +//**************************************************************************** + +// "Editor Commands". Key strokes are translated from a table into these +// I used constants instead of a set so that additional commands could be +// added in descendants (you can't extend a set) + +// There are two ranges of editor commands: the ecViewXXX commands are always +// valid, while the ecEditXXX commands are ignored when the editor is in +// read-only mode + +enum class SynEditorCommand { + ecNone = 0, // Nothing. Useful for user event to handle command + ecViewCommandFirst = 0, + ecViewCommandLast = 500, + ecEditCommandFirst = 501, + ecEditCommandLast = 1000, + + ecLeft = 1, // Move cursor left one char + ecRight = 2, // Move cursor right one char + ecUp = 3, // Move cursor up one line + ecDown = 4, // Move cursor down one line + ecWordLeft = 5, // Move cursor left one word + ecWordRight = 6, // Move cursor right one word + ecLineStart = 7, // Move cursor to beginning of line + ecLineEnd = 8, // Move cursor to end of line + ecPageUp = 9, // Move cursor up one page + ecPageDown = 10, // Move cursor down one page + ecPageLeft = 11, // Move cursor right one page + ecPageRight = 12, // Move cursor left one page + ecPageTop = 13, // Move cursor to top of page + ecPageBottom = 14, // Move cursor to bottom of page + ecEditorTop = 15, // Move cursor to absolute beginning + ecEditorBottom = 16, // Move cursor to absolute end + ecGotoXY = 17, // Move cursor to specific coordinates, Data = PPoint + +//****************************************************************************** +// Maybe the command processor should just take a boolean that signifies if +// selection is affected or not? +//****************************************************************************** + + ecSelection = 100, // Add this to ecXXX command to get equivalent + // command, but with selection enabled. This is not + // a command itself. +// Same as commands above, except they affect selection, too + ecSelLeft = ecLeft + ecSelection, + ecSelRight = ecRight + ecSelection, + ecSelUp = ecUp + ecSelection, + ecSelDown = ecDown + ecSelection, + ecSelWordLeft = ecWordLeft + ecSelection, + ecSelWordRight = ecWordRight + ecSelection, + ecSelLineStart = ecLineStart + ecSelection, + ecSelLineEnd = ecLineEnd + ecSelection, + ecSelPageUp = ecPageUp + ecSelection, + ecSelPageDown = ecPageDown + ecSelection, + ecSelPageLeft = ecPageLeft + ecSelection, + ecSelPageRight = ecPageRight + ecSelection, + ecSelPageTop = ecPageTop + ecSelection, + ecSelPageBottom = ecPageBottom + ecSelection, + ecSelEditorTop = ecEditorTop + ecSelection, + ecSelEditorBottom = ecEditorBottom + ecSelection, + ecSelGotoXY = ecGotoXY + ecSelection, // Data = PPoint + + ecSelWord = 198, + ecSelectAll = 199, // Select entire contents of editor, cursor to end + + ecCopy = 201, // Copy selection to clipboard + + ecScrollUp = 211, // Scroll up one line leaving cursor position unchanged. + ecScrollDown = 212, // Scroll down one line leaving cursor position unchanged. + ecScrollLeft = 213, // Scroll left one char leaving cursor position unchanged. + ecScrollRight = 214, // Scroll right one char leaving cursor position unchanged. + + ecInsertMode = 221, // Set insert mode + ecOverwriteMode = 222, // Set overwrite mode + ecToggleMode = 223, // Toggle ins/ovr mode + + ecNormalSelect = 231, // Normal selection mode + ecColumnSelect = 232, // Column selection mode + ecLineSelect = 233, // Line selection mode + + ecMatchBracket = 250, // Go to matching bracket + + ecGotoMarker0 = 301, // Goto marker + ecGotoMarker1 = 302, // Goto marker + ecGotoMarker2 = 303, // Goto marker + ecGotoMarker3 = 304, // Goto marker + ecGotoMarker4 = 305, // Goto marker + ecGotoMarker5 = 306, // Goto marker + ecGotoMarker6 = 307, // Goto marker + ecGotoMarker7 = 308, // Goto marker + ecGotoMarker8 = 309, // Goto marker + ecGotoMarker9 = 310, // Goto marker + ecSetMarker0 = 351, // Set marker, Data = PPoint - X, Y Pos + ecSetMarker1 = 352, // Set marker, Data = PPoint - X, Y Pos + ecSetMarker2 = 353, // Set marker, Data = PPoint - X, Y Pos + ecSetMarker3 = 354, // Set marker, Data = PPoint - X, Y Pos + ecSetMarker4 = 355, // Set marker, Data = PPoint - X, Y Pos + ecSetMarker5 = 356, // Set marker, Data = PPoint - X, Y Pos + ecSetMarker6 = 357, // Set marker, Data = PPoint - X, Y Pos + ecSetMarker7 = 358, // Set marker, Data = PPoint - X, Y Pos + ecSetMarker8 = 359, // Set marker, Data = PPoint - X, Y Pos + ecSetMarker9 = 360, // Set marker, Data = PPoint - X, Y Pos + + ecGotFocus = 480, + ecLostFocus = 481, + + ecContextHelp = 490, // Help on Word, Data = Word + + ecDeleteLastChar = 501, // Delete last char (i.e. backspace key) + ecDeleteChar = 502, // Delete char at cursor (i.e. delete key) + ecDeleteWord = 503, // Delete from cursor to end of word + ecDeleteLastWord = 504, // Delete from cursor to start of word + ecDeleteBOL = 505, // Delete from cursor to beginning of line + ecDeleteEOL = 506, // Delete from cursor to end of line + ecDeleteLine = 507, // Delete current line + ecClearAll = 508, // Delete everything + ecLineBreak = 509, // Break line at current position, move caret to new line + ecInsertLine = 510, // Break line at current position, leave caret + ecChar = 511, // Insert a character at current position + ecDuplicateLine = 512, // Duplicate current line + ecMoveSelUp = 513, // Move selection up + ecMoveSelDown = 514, // Move selection down + ecImeStr = 550, // Insert character(s) from IME + + ecUndo = 601, // Perform undo if available + ecRedo = 602, // Perform redo if available + ecCut = 603, // Cut selection to clipboard + ecPaste = 604, // Paste clipboard to current position + + ecBlockIndent = 610, // Indent selection + ecBlockUnindent = 611, // Unindent selection + ecTab = 612, // Tab key + ecShiftTab = 613, // Shift+Tab key + ecComment = 614, + ecUncomment = 615, + ecToggleComment = 616, + ecCommentInline = 617, + + ecAutoCompletion = 650, + + ecUpperCase = 620, // apply to the current or previous word + ecLowerCase = 621, + ecToggleCase = 622, + ecTitleCase = 623, + ecUpperCaseBlock = 625, // apply to current selection, or current char if no selection + ecLowerCaseBlock = 626, + ecToggleCaseBlock = 627, + + ecString = 630, //Insert a whole string + + ecUserFirst = 1001, // Start of user-defined commands + + //### Code Folding ### + ecCollapse = ecUserFirst + 100, + ecUncollapse = ecUserFirst + 101, + ecCollapseLevel = ecUserFirst + 102, + ecUncollapseLevel = ecUserFirst + 103, + ecCollapseAll = ecUserFirst + 104, + ecUncollapseAll = ecUserFirst + 105, + //### End Code Folding ### +}; + +class SynKeyError: public BaseError { +public: + explicit SynKeyError(const QString& reason); +}; + +class SynEditKeyStroke +{ +public: + explicit SynEditKeyStroke(); + QKeySequence keySequence() const; + void setKeySequence(QKeySequence& keySequence); + int key() const; + void setKey(int key); + + Qt::KeyboardModifiers keyModifiers() const; + void setKeyModifiers(const Qt::KeyboardModifiers &keyModifiers); + + int key2() const; + void setKey2(int key2); + + Qt::KeyboardModifiers keyModifiers2() const; + void setKeyModifiers2(const Qt::KeyboardModifiers &keyModifiers2); + + SynEditorCommand command() const; + void setCommand(const SynEditorCommand &command); + +private: + int mKey; // Virtual keycode, i.e. VK_xxx + Qt::KeyboardModifiers mKeyModifiers; + int mKey2; + Qt::KeyboardModifiers mKeyModifiers2; + SynEditorCommand mCommand; + +}; + +using PSynEditKeyStroke = std::shared_ptr; +using SynEditKeyStrokeList = QList; + +class SynEditKeyStrokes { +public: + PSynEditKeyStroke add(int key, Qt::KeyboardModifiers modifiers, SynEditorCommand command); + PSynEditKeyStroke findCommand(SynEditorCommand command); + PSynEditKeyStroke findKeycode(int key, Qt::KeyboardModifiers modifiers); + PSynEditKeyStroke findKeycode2(int key, Qt::KeyboardModifiers modifiers, + int key2, Qt::KeyboardModifiers modifiers2); + PSynEditKeyStroke findKeySequence(const QKeySequence& keySeq); + SynEditKeyStrokeList mList; +}; + +#endif // SYNEDITKEYSTROKE_H diff --git a/RedPandaIDE/qsynedit/MiscClasses.cpp b/RedPandaIDE/qsynedit/MiscClasses.cpp index 31467a60..a06dc737 100644 --- a/RedPandaIDE/qsynedit/MiscClasses.cpp +++ b/RedPandaIDE/qsynedit/MiscClasses.cpp @@ -304,3 +304,177 @@ void SynGutter::setBorderColor(const QColor &value) } } + +SynEditMark::SynEditMark(QObject *parent) +{ + mBookmarkNum = -1; + +} + +int SynEditMark::Char() const +{ + return mChar; +} + +void SynEditMark::setChar(int value) +{ + if (value != mChar) { + mChar = value; + } +} + +int SynEditMark::image() const +{ + return mImage; +} + +void SynEditMark::setImage(int image) +{ + if (mImage != image) { + mImage = image; + if (mVisible) + emit changed(); + } +} + +bool SynEditMark::visible() const +{ + return mVisible; +} + +void SynEditMark::setVisible(bool visible) +{ + if (mVisible!=visible) { + mVisible = visible; + emit changed(); + } +} + +int SynEditMark::bookmarkNum() const +{ + return mBookmarkNum; +} + +void SynEditMark::setBookmarkNum(int bookmarkNum) +{ + mBookmarkNum = bookmarkNum; +} + +bool SynEditMark::internalImage() const +{ + return mInternalImage; +} + +void SynEditMark::setInternalImage(bool internalImage) +{ + if (mInternalImage!=internalImage) { + mInternalImage = internalImage; + if (mVisible) + emit changed(); + } +} + +bool SynEditMark::isBookmark() const +{ + return (mBookmarkNum>=0); +} + +int SynEditMark::line() const +{ + return mLine; +} + +void SynEditMark::setLine(int line) +{ + if (mLine!=line) { + if (mVisible && mLine>0) + emit changed(); + mLine = line; + if (mVisible && mLine>0) + emit changed(); + } +} + +SynBookMarkOpt::SynBookMarkOpt(QObject *parent) +{ + mDrawBookmarksFirst = true; + mEnableKeys = true; + mGlyphsVisible = true; + mLeftMargin = 2; + mXOffset = 12; +} + +PSynIconList SynBookMarkOpt::bookmarkImages() const +{ + return mBookmarkImages; +} + +void SynBookMarkOpt::setBookmarkImages(const PSynIconList &images) +{ + if (mBookmarkImages != images) { + mBookmarkImages = images; + emit changed(); + } +} + +bool SynBookMarkOpt::drawBookmarksFirst() const +{ + return mDrawBookmarksFirst; +} + +void SynBookMarkOpt::setDrawBookmarksFirst(bool drawBookmarksFirst) +{ + if (mDrawBookmarksFirst != drawBookmarksFirst) { + mDrawBookmarksFirst = drawBookmarksFirst; + emit changed(); + } +} + +bool SynBookMarkOpt::enableKeys() const +{ + return mEnableKeys; +} + +void SynBookMarkOpt::setEnableKeys(bool enableKeys) +{ + mEnableKeys = enableKeys; +} + +bool SynBookMarkOpt::glyphsVisible() const +{ + return mGlyphsVisible; +} + +void SynBookMarkOpt::setGlyphsVisible(bool glyphsVisible) +{ + if (mGlyphsVisible!=glyphsVisible) { + mGlyphsVisible = glyphsVisible; + emit changed(); + } +} + +int SynBookMarkOpt::leftMargin() const +{ + return mLeftMargin; +} + +void SynBookMarkOpt::setLeftMargin(int leftMargin) +{ + if (leftMargin!=mLeftMargin) { + mLeftMargin = leftMargin; + emit changed(); + } +} + +int SynBookMarkOpt::xOffset() const +{ + return mXOffset; +} + +void SynBookMarkOpt::setXOffset(int xOffset) +{ + if (mXOffset!=xOffset) { + mXOffset = xOffset; + emit changed(); + } +} diff --git a/RedPandaIDE/qsynedit/MiscClasses.h b/RedPandaIDE/qsynedit/MiscClasses.h index ea29aa14..a8525e96 100644 --- a/RedPandaIDE/qsynedit/MiscClasses.h +++ b/RedPandaIDE/qsynedit/MiscClasses.h @@ -4,6 +4,7 @@ #include #include #include +#include "Types.h" enum class SynGutterBorderStyle { None, @@ -99,7 +100,84 @@ private: bool mVisible; int mWidth; int mAutoSizeDigitCount; +}; + +using PSynGutter = std::shared_ptr; + +class SynEditMark : public QObject { + Q_OBJECT +public: + explicit SynEditMark(QObject* parent = nullptr); + int Char() const; + void setChar(int value); + + int image() const; + void setImage(int image); + + bool visible() const; + void setVisible(bool visible); + + int bookmarkNum() const; + void setBookmarkNum(int bookmarkNum); + + bool internalImage() const; + void setInternalImage(bool internalImage); + + bool isBookmark() const ; + + int line() const; + void setLine(int line); + +signals: + void changed(); +protected: + int mLine; + int mChar; + int mImage; + bool mVisible; + bool mInternalImage; + int mBookmarkNum; }; +using PSynEditMark = std::shared_ptr; + +using SynEditMarkList = QList; + +using PSynEditMarkList = std::shared_ptr; + +class SynBookMarkOpt: public QObject { + Q_OBJECT +public: + explicit SynBookMarkOpt(QObject* parent=nullptr); + PSynIconList bookmarkImages() const; + void setBookmarkImages(const PSynIconList &images); + + bool drawBookmarksFirst() const; + void setDrawBookmarksFirst(bool drawBookmarksFirst); + + bool enableKeys() const; + void setEnableKeys(bool enableKeys); + + bool glyphsVisible() const; + void setGlyphsVisible(bool glyphsVisible); + + int leftMargin() const; + void setLeftMargin(int leftMargin); + + int xOffset() const; + void setXOffset(int xOffset); + +signals: + void changed(); +private: + PSynIconList mBookmarkImages; + bool mDrawBookmarksFirst; + bool mEnableKeys; + bool mGlyphsVisible; + int mLeftMargin; + int mXOffset; +}; + +using PSynBookMarkOpt = std::shared_ptr; #endif // MISCCLASSES_H diff --git a/RedPandaIDE/qsynedit/SynEdit.cpp b/RedPandaIDE/qsynedit/SynEdit.cpp index 0e91b21f..1930c7ba 100644 --- a/RedPandaIDE/qsynedit/SynEdit.cpp +++ b/RedPandaIDE/qsynedit/SynEdit.cpp @@ -1,6 +1,602 @@ #include "SynEdit.h" +#include +#include +#include -SynEdit::SynEdit(QWidget *parent) : QWidget(parent) +SynEdit::SynEdit(QWidget *parent, Qt::WindowFlags f) : QFrame(parent,f) { + mPaintLock = 0; + mPainting = false; + mLines = std::make_shared(); + mOrigLines = mLines; + //fPlugins := TList.Create; + mMouseMoved = false; + mUndoing = false; + mLines->connect(mLines.get(), &SynEditStringList::changed, this, &SynEdit::linesChanged); + mLines->connect(mLines.get(), &SynEditStringList::changing, this, &SynEdit::linesChanging); + mLines->connect(mLines.get(), &SynEditStringList::cleared, this, &SynEdit::linesCleared); + mLines->connect(mLines.get(), &SynEditStringList::deleted, this, &SynEdit::linesDeleted); + mLines->connect(mLines.get(), &SynEditStringList::inserted, this, &SynEdit::linesInserted); + mLines->connect(mLines.get(), &SynEditStringList::putted, this, &SynEdit::linesPutted); +#ifdef Q_OS_WIN + mFontDummy = QFont("Consolas",10); +#elif Q_OS_LINUX + mFontDummy = QFont("terminal",14); +#else +#error "Not supported!" +#endif + mUndoList = std::make_shared(); + mUndoList->connect(mUndoList.get(), &SynEditUndoList::addedUndo, this, &SynEdit::undoAdded); + mOrigUndoList = mUndoList; + mRedoList = std::make_shared(); + mRedoList->connect(mRedoList.get(), &SynEditUndoList::addedUndo, this, &SynEdit::redoAdded); + mOrigRedoList = mRedoList; + //DoubleBuffered = false; + mActiveLineColor = QColor(); + mSelectedBackground = QColor(); + mSelectedForeground = QColor(); + + mBookMarkOpt.connect(&mBookMarkOpt, &SynBookMarkOpt::changed, this, &SynEdit::bookMarkOptionsChanged); + // fRightEdge has to be set before FontChanged is called for the first time + mRightEdge = 80; + mGutter.setRightOffset(21); + mGutter.connect(&mGutter, &SynGutter::changed, this, &SynEdit::gutterChanged); + mGutterWidth = mGutter.width(); + mTextOffset = mGutterWidth + 2; + //ControlStyle := ControlStyle + [csOpaque, csSetCaption, csNeedsBorderPaint]; + //Height := 150; + //Width := 200; + this->setCursor(Qt::CursorShape::IBeamCursor); + //TabStop := True; + mInserting = true; + mMaxScrollWidth = 1024; + mScrollBars = SynScrollStyle::ssBoth; + this->setFrameShape(QFrame::Panel); + this->setFrameShadow(QFrame::Sunken); + this->setLineWidth(1); + mInsertCaret = SynEditCaretType::ctVerticalLine; + mOverwriteCaret = SynEditCaretType::ctBlock; + mSelectionMode = SynSelectionMode::smNormal; + mActiveSelectionMode = SynSelectionMode::smNormal; + //stop qt to auto fill background + setAutoFillBackground(false); + //fFocusList := TList.Create; + //fKbdHandler := TSynEditKbdHandler.Create; + //fMarkList.OnChange := MarkListChange; + setDefaultKeystrokes(); + mRightEdgeColor = QColorConstants::Svg::silver; + /* IME input */ + mImeCount = 0; + mMBCSStepAside = false; + /* end of IME input */ + mWantReturns = true; + mWantTabs = false; + mTabWidth = 4; + mLeftChar = 1; + mTopLine = 1; + mCaretX = 1; + mLastCaretX = 1; + mCaretY = 1; + mBlockBegin.Char = 1; + mBlockBegin.Line = 1; + mBlockEnd = mBlockBegin; + mOptions = eoAutoIndent | eoDragDropEditing | eoEnhanceEndKey | + eoScrollPastEol | eoShowScrollHint | eoSmartTabs | eoTabsToSpaces | + eoSmartTabDelete| eoGroupUndo; + mScrollTimer = new QTimer(this); + mScrollTimer->setInterval(100); + connect(mScrollTimer, &QTimer::timeout,this, &SynEdit::scrollTimerHandler); + + mScrollHintColor = QColorConstants::Yellow; + mScrollHintFormat = SynScrollHintFormat::shfTopLineOnly; + + synFontChanged(); } + +int SynEdit::displayLineCount() +{ + return lineToRow(mLines->count()); +} + +BufferCoord SynEdit::caretXY() +{ + BufferCoord result; + result.Char = caretX(); + result.Line = caretY(); + return result; +} + +int SynEdit::caretX() +{ + return mCaretX; +} + +int SynEdit::caretY() +{ + return mCaretY; +} + +void SynEdit::setCaretXY(const BufferCoord &value) +{ + setCaretXYCentered(false,value); +} + +void SynEdit::setCaretXYEx(bool CallEnsureCursorPos, BufferCoord value) +{ + bool vTriggerPaint=true; //how to test it? + + if (vTriggerPaint) + doOnPaintTransient(SynTransientType::ttBefore); + int nMaxX = maxScrollWidth() + 1; + if (value.Line > mLines->count()) + value.Line = mLines->count(); + if (value.Line < 1) { + // this is just to make sure if Lines stringlist should be empty + value.Line = 1; + if (!mOptions.testFlag(SynEditorOption::eoScrollPastEol)) { + nMaxX = 1; + } + } else { + if (!mOptions.testFlag(SynEditorOption::eoScrollPastEol)) + nMaxX = mLines->getString(value.Line-1).length(); + } + if ((value.Char > nMaxX) && (! (mOptions.testFlag(SynEditorOption::eoScrollPastEol)) || + !(mOptions.testFlag(SynEditorOption::eoAutoSizeMaxScrollWidth))) ) + value.Char = nMaxX; + if (value.Char < 1) + value.Char = 1; + if ((value.Char != mCaretX) || (value.Line != mCaretY)) { + incPaintLock(); + auto action = finally([this]{ + decPaintLock(); + }); + // simply include the flags, fPaintLock is > 0 + if (mCaretX != value.Char) { + mCaretX = value.Char; + mStatusChanges.setFlag(SynStatusChange::scCaretX); + } + if (mCaretY != value.Line) { + if (!mActiveLineColor.isValid()) { + invalidateLine(value.Line); + invalidateLine(mCaretY); + } + mCaretY = value.Line; + mStatusChanges.setFlag(SynStatusChange::scCaretY); + } + // Call UpdateLastCaretX before DecPaintLock because the event handler it + // calls could raise an exception, and we don't want fLastCaretX to be + // left in an undefined state if that happens. + updateLastCaretX(); + if (CallEnsureCursorPos) + ensureCursorPosVisible(); + mStateFlags.setFlag(SynStateFlag::sfCaretChanged); + mStateFlags.setFlag(SynStateFlag::sfScrollbarChanged); + } else { + // Also call UpdateLastCaretX if the caret didn't move. Apps don't know + // anything about fLastCaretX and they shouldn't need to. So, to avoid any + // unwanted surprises, always update fLastCaretX whenever CaretXY is + // assigned to. + // Note to SynEdit developers: If this is undesirable in some obscure + // case, just save the value of fLastCaretX before assigning to CaretXY and + // restore it afterward as appropriate. + updateLastCaretX(); + } + if (vTriggerPaint) + doOnPaintTransient(SynTransientType::ttAfter); + +} + +void SynEdit::setCaretXYCentered(bool ForceToMiddle, const BufferCoord &value) +{ + incPaintLock(); + auto action = finally([this] { + decPaintLock(); + }); + mStatusChanges.setFlag(SynStatusChange::scSelection); + setCaretXYEx(ForceToMiddle,value); + if selAvail() then + invalidateSelection(); + mBlockBegin.Char = mCaretX; + mBlockBegin.Line = mCaretY; + mBlockEnd = mBlockBegin; + if (ForceToMiddle) + ensureCursorPosVisibleEx(true); // but here after block has been set + +} + +void SynEdit::invalidateGutter() +{ + invalidateGutterLines(-1, -1); +} + +void SynEdit::invalidateGutterLine(int aLine) +{ + if ((aLine < 1) || (aLine > mLines->count())) + return; + + invalidateGutterLines(aLine, aLine); +} + +void SynEdit::invalidateGutterLines(int FirstLine, int LastLine) +{ + QRect rcInval; + if (!isVisible()) + return; + if (FirstLine == -1 && LastLine == -1) { + rcInval = QRect(clientLeft(), clientTop(), mGutterWidth, clientHeight()); + if (mStateFlags.testFlag(SynStateFlag::sfLinesChanging)) + mInvalidateRect = mInvalidateRect.united(rcInval); + else + update(rcInval); + } else { + // find the visible lines first + if (LastLine < FirstLine) + std::swap(LastLine, FirstLine); + if (mUseCodeFolding) { + FirstLine = lineToRow(FirstLine); + if (LastLine <= mLines->count()) + LastLine = lineToRow(LastLine); + else + LastLine = INT_MAX; + } + FirstLine = std::max(FirstLine, topLine()); + LastLine = std::min(LastLine, topLine() + linesInWindow()); + // any line visible? + if (LastLine >= FirstLine) { + rcInval = {clientLeft(), clientTop()+mTextHeight * (FirstLine - topLine()), + mGutterWidth, mTextHeight * (LastLine - topLine() + 1)}; + if (mStateFlags.testFlag(SynStateFlag::sfLinesChanging)) { + mInvalidateRect = mInvalidateRect.united(rcInval); + } else { + update(rcInval); + } + } + } +} + +void SynEdit::clearAreaList(SynEditingAreaList areaList) +{ + areaList.clear(); +} + +void SynEdit::computeCaret(int X, int Y) +{ + DisplayCoord vCaretNearestPos = pixelsToNearestRowColumn(X, Y); + vCaretNearestPos.Row = MinMax(vCaretNearestPos.Row, 1, displayLineCount()); + setInternalDisplayXY(vCaretNearestPos); +} + +void SynEdit::computeScroll(int X, int Y) +{ + QRect iScrollBounds; // relative to the client area + // don't scroll if dragging text from other control +// if (not MouseCapture) and (not Dragging) then begin +// fScrollTimer.Enabled := False; +// Exit; +// end; + + iScrollBounds = QRect(mGutterWidth+this->frameWidth(), this->frameWidth(), mCharsInWindow * mCharWidth, + mLinesInWindow * mTextHeight); + + if (X < iScrollBounds.left()) + mScrollDeltaX = (X - iScrollBounds.left()) % mCharWidth - 1; + else if (X >= iScrollBounds.right()) + mScrollDeltaX = (X - iScrollBounds.right()) % mCharWidth + 1; + else + mScrollDeltaX = 0; + + if (Y < iScrollBounds.top()) + mScrollDeltaY = (Y - iScrollBounds.top()) % mTextHeight - 1; + else if (Y >= iScrollBounds.bottom()) + mScrollDeltaY = (Y - iScrollBounds.bottom()) % mTextHeight + 1; + else + mScrollDeltaY = 0; + + if (mScrollDeltaX!=0 || mScrollDeltaY!=0) + mScrollTimer->start(); +} + +void SynEdit::doBlockIndent() +{ + BufferCoord OrgCaretPos; + BufferCoord BB, BE; + QString StrToInsert; + int Run; + int e,x,i,InsertStrLen; + QString Spaces; + SynSelectionMode OrgSelectionMode; + BufferCoord InsertionPos; + + OrgSelectionMode = mActiveSelectionMode; + OrgCaretPos = caretXY(); + StrToInsert = nullptr; + if (selAvail()) { + auto action = finally([&,this]{ + if (BE.Char > 1) + BE.Char+=Spaces.length(); + setCaretAndSelection(OrgCaretPos, + {BB.Char + Spaces.length(), BB.Line}, BE); + setActiveSelectionMode(OrgSelectionMode); + }); + // keep current selection detail + BB = blockBegin(); + BE = blockEnd(); + // build text to insert + if (BE.Char == 1) { + e = BE.Line - 1; + x = 1; + } else { + e = BE.Line; + if (mOptions.testFlag(SynEditorOption::eoTabsToSpaces)) + x = caretX() + mTabWidth; + else + x = caretX() + 1; + } + if (mOptions.testFlag(eoTabsToSpaces)) { + InsertStrLen = (mTabWidth + 2) * (e - BB.Line) + mTabWidth + 1; + // chars per line * lines-1 + last line + null char + StrToInsert.resize(InsertStrLen); + Run = 0; + Spaces = QString(mTabWidth,' ') ; + } else { + InsertStrLen = 3 * (e - BB.Line) + 2; + // #9#13#10 * lines-1 + (last line's #9 + null char) + StrToInsert.resize(InsertStrLen); + Run = 0; + Spaces = "\t"; + } + for (i = BB.Line; iBeginBlock(); + auto action2=finally([this]{ + mUndoList->EndBlock(); + }); + InsertionPos.Line = BB.Line; + if (mActiveSelectionMode == SynSelectionMode::smColumn) + InsertionPos.Char = std::min(BB.Char, BE.Char); + else + InsertionPos.Char = 1; + insertBlock(InsertionPos, InsertionPos, StrToInsert, true); + mUndoList->AddChange(SynChangeReason::crIndent, BB, BE, "", SynSelectionMode::smColumn); + //We need to save the position of the end block for redo + mUndoList.AddChange(SynChangeReason::crIndent, + {BB.Char + Spaces.length(), BB.Line}, + {BE.Char + Spaces.length(), BE.Line}, + "", SynSelectionMode::smColumn); + //adjust the x position of orgcaretpos appropriately + OrgCaretPos.Char = X; + } + } + +} + +void SynEdit::incPaintLock() +{ + mPaintLock ++ ; +} + +void SynEdit::decPaintLock() +{ + Q_ASSERT(mPaintLock > 0); + mPaintLock--; + if (mPaintLock == 0 ) { + if (mStateFlags.testFlag(SynStateFlag::sfScrollbarChanged)) + updateScrollbars(); + if (mStateFlags.testFlag(SynStateFlag::sfCaretChanged)) + updateCaret(); + if (mStatusChanges!=0) + doOnStatusChange(mStatusChanges); + } +} + +bool SynEdit::mouseCapture() +{ + return hasMouseTracking(); +} + +int SynEdit::clientWidth() +{ + return frameRect().width()-2*frameWidth(); +} + +int SynEdit::clientHeight() +{ + return frameRect().height()-2*frameWidth(); +} + +int SynEdit::clientTop() +{ + return frameRect().top()+frameWidth(); +} + +int SynEdit::clientLeft() +{ + return frameRect().left()+frameWidth(); +} + +QRect SynEdit::clientRect() +{ + return QRect(frameRect().left()+frameWidth(),frameRect().top()+frameWidth(), frameRect().width()-2*frameWidth(), frameRect().height()-2*frameWidth()); +} + +void SynEdit::bookMarkOptionsChanged() +{ + invalidateGutter(); +} + +void SynEdit::linesChanged() +{ + SynSelectionMode vOldMode; + mStateFlags.setFlag(SynStateFlag::sfLinesChanging, false); + if (mUseCodeFolding) + rescan(); + + updateScrollBars(); + vOldMode = mActiveSelectionMode; + setBlockBegin(caretXY()); + mActiveSelectionMode = vOldMode; + update(mInvalidateRect); + mInvalidateRect = {0,0,0,0}; + + if (mGutter.showLineNumbers() && (mGutter.autoSize())) + mGutter.autoSizeDigitCount(mLines->count()); + if (!mOptions.testFlag(SynEditorOption::eoScrollPastEof)) + setTopLine(mTopLine); +} + +void SynEdit::linesChanging() +{ + mStateFlags.setFlag(SynStateFlag::sfLinesChanging); +} + +void SynEdit::linesCleared() +{ + if (mUseCodeFolding) + foldOnListCleared(); + clearUndo(); + // invalidate the *whole* client area + mInvalidateRect={0,0,0,0}; + update(); + // set caret and selected block to start of text + setCaretXY({1,1}); + // scroll to start of text + setTopLine(1); + setLeftChar(1); + mStatusChanges.setFlag(SynStatusChange::scAll); +} + +void SynEdit::linesDeleted(int index, int count) +{ + if (mUseCodeFolding) + foldOnListDeleted(index + 1, count); + if (mHighlighter && mLines->count() > 0) + scanFrom(index); + invalidateLines(index + 1, INT_MAX); + invalidateGutterLines(index + 1, INT_MAX); +} + +void SynEdit::linesInserted(int index, int count) +{ + if (mUseCodeFolding) + foldOnListInserted(index + 1, count); + if (mHighlighter && mLines->count() > 0) { + int vLastScan = index; + do { + vLastScan = scanFrom(vLastScan); + vLastScan++; + } while (vLastScan < index + count) ; + } + invalidateLines(index + 1, INT_MAX); + invalidateGutterLines(index + 1, INT_MAX); + if (mOptions.setFlag(SynEditorOption::eoAutoSizeMaxScrollWidth)) { + int L = mLines->expandedStringLength(index); + if (L > mMaxScrollWidth) + setMaxScrollWidth(L); + } +} + +void SynEdit::linesPutted(int index, int count) +{ + int vEndLine = index + 1; + if (mHighlighter) { + vEndLine = std::max(vEndLine, scanFrom(index) + 1); + // If this editor is chained then the real owner of text buffer will probably + // have already parsed the changes, so ScanFrom will return immediately. + if (mLines != mOrigLines) + vEndLine = INT_MAX; + } + invalidateLines(index + 1, vEndLine); + + if (mOptions.setFlag(SynEditorOption::eoAutoSizeMaxScrollWidth)) { + int L = mLines->expandedStringLength(index); + if (L > mMaxScrollWidth) + setMaxScrollWidth(L); + } +} + +void SynEdit::undoAdded() +{ + updateModifiedStatus(); + + // we have to clear the redo information, since adding undo info removes + // the necessary context to undo earlier edit actions + if (! mUndoList->insideRedo() && + mUndoList->PeekItem() && (mUndoList->PeekItem()->changeReason()!=SynChangeReason::crGroupBreak)) + mRedoList->Clear(); + if (mUndoList->blockCount() == 0 ) + doChange(); +} + +void SynEdit::redoAdded() +{ + updateModifiedStatus(); + + if (mRedoList->blockCount() == 0 ) + doChange(); +} + +void SynEdit::gutterChanged() +{ + if (mGutter.showLineNumbers() && mGutter.autoSize()) + mGutter.autoSizeDigitCount(mLines->count()); + int nW; + if (mGutter.useFontStyle()) { + QFontMetrics fm=QFontMetrics(mGutter.font()); + nW = mGutter.realGutterWidth(fm.averageCharWidth()); + } else { + nW = mGutter.realGutterWidth(mCharWidth); + } + if (nW == mGutterWidth) + invalidateGutter(); + else + setGutterWidth(nW); +} + +void SynEdit::scrollTimerHandler() +{ + QPoint iMousePos; + DisplayCoord C; + int X, Y; + + iMousePos = QCursor::pos(); + iMousePos = mapFromGlobal(iMousePos); + C = pixelsToRowColumn(iMousePos.x(), iMousePos.y()); + C.Row = MinMax(C.Row, 1, displayLineCount()); + if (mScrollDeltaX != 0) { + setLeftChar(leftChar() + mScrollDeltaX); + X = leftChar(); + if (mScrollDeltaX > 0) // scrolling right? + X+=charsInWindow(); + C.Column = X; + } + if (mScrollDeltaY != 0) { + if (QApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier)) + setTopLine(topLine() + mScrollDeltaY * linesInWindow()); + else + setTopLine(topLine()) + mScrollDeltaY); + Y = topLine(); + if (mScrollDeltaY > 0) // scrolling down? + Y+=linesInWindow() - 1; + C.Row = MinMax(Y, 1, displayLineCount()); + } + BufferCoord vCaret = displayToBufferPos(C); + if ((caretX() <> vCaret.Char) || (caretY() != vCaret.Line)) { + // changes to line / column in one go + incPaintLock(); + auto action = finally([this]{ + decPaintLock(); + }); + setInternalCaretXY(vCaret); + + // if MouseCapture is True we're changing selection. otherwise we're dragging + if (mouseCapture()) + setBlockEnd(caretXY()); + } + computeScroll(iMousePos.x(), iMousePos.y()); +} diff --git a/RedPandaIDE/qsynedit/SynEdit.h b/RedPandaIDE/qsynedit/SynEdit.h index 543f7910..842fba82 100644 --- a/RedPandaIDE/qsynedit/SynEdit.h +++ b/RedPandaIDE/qsynedit/SynEdit.h @@ -1,16 +1,328 @@ #ifndef SYNEDIT_H #define SYNEDIT_H +#include +#include +#include +#include +#include #include +#include "MiscClasses.h" +#include "CodeFolding.h" +#include "Types.h" +#include "TextBuffer.h" +#include "KeyStrokes.h" -class SynEdit : public QWidget +enum class SynFontSmoothMethod { + None, AntiAlias, ClearType +}; + + +enum class SynScrollHintFormat { + shfTopLineOnly, shfTopToBottom +}; + +enum class SynScrollStyle { + ssNone, ssHorizontal, ssVertical, ssBoth +}; + +enum class SynEditCaretType { + ctVerticalLine, ctHorizontalLine, ctHalfBlock, ctBlock +}; + +enum class SynStatusChange { + scAll = 0x0001, + scCaretX = 0x0002, + scCaretY = 0x0004, + scLeftChar = 0x0008, + scTopLine = 0x0010, + scInsertMode = 0x0020, + scModified = 0x0040, + scSelection = 0x0080, + scReadOnly = 0x0100, + scOpenFile = 0x0200 +}; + +Q_DECLARE_FLAGS(SynStatusChanges, SynStatusChange) +Q_DECLARE_OPERATORS_FOR_FLAGS(SynStatusChanges) + +enum class SynStateFlag { + sfCaretChanged = 0x0001, + sfScrollbarChanged = 0x0002, + sfLinesChanging = 0x0004, + sfIgnoreNextChar = 0x0008, + sfCaretVisible = 0x0010, + sfDblClicked = 0x0020, + sfWaitForDragging = 0x0040 +}; + +Q_DECLARE_FLAGS(SynStateFlags,SynStateFlag) + +Q_DECLARE_OPERATORS_FOR_FLAGS(SynStateFlags) + +enum SynEditorOption { + eoAltSetsColumnMode = 0x00000001, //Holding down the Alt Key will put the selection mode into columnar format + eoAutoIndent = 0x00000002, //Will indent the caret on new lines with the same amount of leading white space as the preceding line + eoAddIndent = 0x00000004, //Will add one tab width of indent when typing { and :, and remove the same amount when typing } + eoAutoSizeMaxScrollWidth = 0x00000008, //Automatically resizes the MaxScrollWidth property when inserting text + eoDisableScrollArrows = 0x00000010 , //Disables the scroll bar arrow buttons when you can't scroll in that direction any more + eoDragDropEditing = 0x00000020, //Allows you to select a block of text and drag it within the document to another location + eoDropFiles = 0x00000040, //Allows the editor accept OLE file drops + eoEnhanceHomeKey = 0x00000080, //enhances home key positioning, similar to visual studio + eoEnhanceEndKey = 0x00000100, //enhances End key positioning, similar to JDeveloper + eoGroupUndo = 0x00000200, //When undoing/redoing actions, handle all continous changes of the same kind in one call instead undoing/redoing each command separately + eoHalfPageScroll = 0x00000400, //When scrolling with page-up and page-down commands, only scroll a half page at a time + eoHideShowScrollbars = 0x00000800, //if enabled, then the scrollbars will only show when necessary. If you have ScrollPastEOL, then it the horizontal bar will always be there (it uses MaxLength instead) + eoKeepCaretX = 0x00001000 , //When moving through lines w/o Cursor Past EOL, keeps the X position of the cursor + eoNoCaret = 0x00002000, //Makes it so the caret is never visible + eoNoSelection = 0x00004000, //Disables selecting text + eoRightMouseMovesCursor = 0x00008000, //When clicking with the right mouse for a popup menu, move the cursor to that location + eoScrollByOneLess = 0x00010000, //Forces scrolling to be one less + eoScrollHintFollows = 0x00020000, //The scroll hint follows the mouse when scrolling vertically + eoScrollPastEof = 0x00040000, //Allows the cursor to go past the end of file marker + eoScrollPastEol = 0x00080000, //Allows the cursor to go past the last character into the white space at the end of a line + eoShowScrollHint = 0x00100000, //Shows a hint of the visible line numbers when scrolling vertically + eoShowSpecialChars = 0x00200000, //Shows the special Characters + eoSmartTabDelete = 0x00400000, //similar to Smart Tabs, but when you delete characters + eoSmartTabs = 0x00800000, //When tabbing, the cursor will go to the next non-white space character of the previous line + eoSpecialLineDefaultFg = 0x01000000, //disables the foreground text color override when using the OnSpecialLineColor event + eoTabIndent = 0x02000000, //When active and act as block indent, unindent when text is selected + eoTabsToSpaces = 0x04000000, //Converts a tab character to a specified number of space characters + eoShowRainbowColor = 0x08000000, + eoTrimTrailingSpaces = 0x10000000 //Spaces at the end of lines will be trimmed and not saved +}; + +Q_DECLARE_FLAGS(SynEditorOptions, SynEditorOption) + +Q_DECLARE_OPERATORS_FOR_FLAGS(SynEditorOptions) + +enum class SynReplaceAction { + raCancel, raSkip, raReplace, raReplaceAll +}; + +struct SynEditingArea { + int beginX; + int endX; + QColor color; +}; + + +using PSynEditingArea = std::shared_ptr; +using SynEditingAreaList = QList; +enum class SynEditingAreaType { + eatRectangleBorder, + eatWaveUnderLine, + eatUnderLine +}; + +enum class SynTransientType { + ttBefore, ttAfter +}; + +enum class SynScrollBarKind { + sbHorizontal, sbVertical +}; + +using SynPaintTransientProc = std::function; +using SynPlaceMarkProc = std::function; +using SynProcessCommandProc = std::function; +using SynMouseCursorProc = std::function; +using SynPaintProc = std::function; +using SynPreparePaintHighlightTokenProc = std::function; +using SynReplaceTextProc = std::function; +using SynSpecialLineColorsProc = std::function; +using SynEditingAreasProc = std::function; +using SynGutterGetTextProc = std::function; +using SynTGutterPaintProc = std::function; + +class SynEdit; +using PSynEdit = std::shared_ptr; + +class SynEdit : public QFrame { Q_OBJECT public: - explicit SynEdit(QWidget *parent = nullptr); + explicit SynEdit(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); + + int displayLineCount(); + BufferCoord caretXY(); + int caretX(); + int caretY(); + + void setCaretXY(const BufferCoord& value); + void setCaretXYEx(bool CallEnsureCursorPos, BufferCoord value); + void setCaretXYCentered(bool ForceToMiddle, const BufferCoord& value); + + void invalidateGutter(); + void invalidateGutterLine(int aLine); + //todo: + void invalidateGutterLines(int FirstLine, int LastLine); + DisplayCoord pixelsToNearestRowColumn(int aX, int aY); + int lineToRow(int aLine); signals: + void Changed(); + void ChainUndoAdded(); + void ChainRedoAdded(); + void ChainLinesChanging(); + void ChainLinesChanged(); + void ChainListCleared(); + + void ChainListDeleted(int Index, int Count); + void ChainListInserted(int Index, int Count); + void ChainListPutted(int Index, int Count); + + void FilesDropped(int X,int Y, const QStringList& AFiles); + void GutterClicked(Qt::MouseButton button, int x, int y, int line, PSynEditMark mark); + void ImeInputed(const QString& s); + + void contextHelp(const QString& word); + + void scrolled(SynScrollBarKind ScrollBar); + void statusChange(SynStatusChanges changes); + +private: + void clearAreaList(SynEditingAreaList areaList); + void computeCaret(int X, int Y); + void computeScroll(int X, int Y); + void doBlockIndent(); + + void incPaintLock(); + void decPaintLock(); + bool mouseCapture(); + int clientWidth(); + int clientHeight(); + int clientTop(); + int clientLeft(); + QRect clientRect(); + + //todo + void setInternalDisplayXY(const DisplayCoord& aPos); + + +private slots: + void bookMarkOptionsChanged(); + void gutterChanged(); + void linesChanged(); + void linesChanging(); + void linesCleared(); + void linesDeleted(int index, int count); + void linesInserted(int index, int count); + void linesPutted(int index, int count); + void redoAdded(); + void scrollTimerHandler(); + void undoAdded(); + +private: + SynEditFoldRanges mAllFoldRanges; + SynEditCodeFolding mCodeFolding; + bool mUseCodeFolding; + bool mAlwaysShowCaret; + BufferCoord mBlockBegin; + BufferCoord mBlockEnd; + int mCaretX; + int mLastCaretX; + int mCaretY; + int mCharsInWindow; + int mCharWidth; + QFont mFontDummy; + SynFontSmoothMethod mFontSmoothing; + bool mMouseMoved; + /* IME input */ + int mImeCount; + bool mMBCSStepAside; + /* end of IME input */ + bool mInserting; + bool mPainting; + PSynEditStringList mLines; + PSynEditStringList mOrigLines; + PSynEditUndoList mOrigUndoList; + PSynEditUndoList mOrigRedoList; + int mLinesInWindow; + int mLeftChar; + int mMaxScrollWidth; + int mPaintLock; + bool mReadOnly; + int mRightEdge; + QColor mRightEdgeColor; + QColor mScrollHintColor; + SynScrollHintFormat mScrollHintFormat; + SynScrollStyle mScrollBars; + int mTextHeight; + int mTextOffset; + int mTopLine; + PSynHighlighter mHighlighter; + QColor mSelectedForeground; + QColor mSelectedBackground; + QColor mActiveLineColor; + PSynEditUndoList mUndoList; + PSynEditUndoList mRedoList; + SynEditMarkList mBookMarks; + int mMouseDownX; + int mMouseDownY; + SynBookMarkOpt mBookMarkOpt; + bool mHideSelection; + int mMouseWheelAccumulator; + SynEditCaretType mOverwriteCaret; + SynEditCaretType mInsertCaret; + QPoint mCaretOffset; + SynEditKeyStrokes mKeyStrokes; + bool mModified; + QDateTime mLastModifyTime; + SynEditMarkList mMarkList; + int mExtraLineSpacing; + SynSelectionMode mSelectionMode; + SynSelectionMode mActiveSelectionMode; //mode of the active selection + bool mWantReturns; + bool mWantTabs; + SynGutter mGutter; + int mTabWidth; + QRect mInvalidateRect; + SynStateFlags mStateFlags; + SynEditorOptions mOptions; + SynStatusChanges mStatusChanges; + int mLastKey; + Qt::KeyboardModifiers mLastKeyModifiers; + //fSearchEngine: TSynEditSearchCustom; + //fHookedCommandHandlers: TList; + //fKbdHandler: TSynEditKbdHandler; + // fFocusList: TList; + // fPlugins: TList; + QTimer* mScrollTimer; + int mScrollDeltaX; + int mScrollDeltaY; + + PSynEdit fChainedEditor; + + bool mShowSpecChar; + int mPaintTransientLock; + bool mIsScrolling; + int mPainterLock; + bool mUndoing; + // event handlers + SynPlaceMarkProc mOnClearMark; + SynProcessCommandProc mOnCommandProcessed; + SynMouseCursorProc mOnMouseCursor; + SynPaintProc mOnPaint; + SynPreparePaintHighlightTokenProc mOnPaintHighlightToken; + SynPlaceMarkProc mOnPlaceMark; + SynProcessCommandProc mOnProcessingCommand; + SynProcessCommandProc mOnProcessingUserCommand; + + SynReplaceTextProc mOnReplaceText; + SynSpecialLineColorsProc mOnSpecialLineColors; + SynEditingAreasProc mOnEditingAreas; + SynPaintTransientProc mOnPaintTransient; + SynGutterGetTextProc mOnGutterGetText; + SynTGutterPaintProc mOnGutterPaint; + int mGutterWidth; }; #endif // SYNEDIT_H diff --git a/RedPandaIDE/qsynedit/TextBuffer.cpp b/RedPandaIDE/qsynedit/TextBuffer.cpp index 85dcf9ba..39423f1e 100644 --- a/RedPandaIDE/qsynedit/TextBuffer.cpp +++ b/RedPandaIDE/qsynedit/TextBuffer.cpp @@ -6,7 +6,8 @@ #include #include "../utils.h" -SynEditStringList::SynEditStringList() +SynEditStringList::SynEditStringList(QObject* parent): + QObject(parent) { mAppendNewLineAtEOF = true; mFileEndingType = FileEndingType::Windows; @@ -159,7 +160,7 @@ void SynEditStringList::setBraceLevel(int Index, int level) endUpdate(); } -QString SynEditStringList::get(int Index) +QString SynEditStringList::getString(int Index) { if (Index<0 || Index>=mList.count()) { return QString(); @@ -355,7 +356,7 @@ QString SynEditStringList::GetTextStr() return Result; } -void SynEditStringList::put(int Index, const QString &s) { +void SynEditStringList::putString(int Index, const QString &s) { if (Index == mList.count()) { add(s); } else { @@ -631,7 +632,7 @@ SynEditStringRec::SynEditStringRec(): } -SynEditUndoList::SynEditUndoList() +SynEditUndoList::SynEditUndoList():QObject() { mMaxUndoActions = 1024; mNextChangeNumber = 1; @@ -644,7 +645,9 @@ SynEditUndoList::SynEditUndoList() mInitialChangeNumber = 0; } -void SynEditUndoList::AddChange(SynChangeReason AReason, const BufferCoord &AStart, const BufferCoord &AEnd, const QString &ChangeText, SynSelectionMode SelMode) +void SynEditUndoList::AddChange(SynChangeReason AReason, const BufferCoord &AStart, + const BufferCoord &AEnd, const QString &ChangeText, + SynSelectionMode SelMode) { if (mLockCount != 0) return; diff --git a/RedPandaIDE/qsynedit/TextBuffer.h b/RedPandaIDE/qsynedit/TextBuffer.h index 84a6c146..1542c0a2 100644 --- a/RedPandaIDE/qsynedit/TextBuffer.h +++ b/RedPandaIDE/qsynedit/TextBuffer.h @@ -48,7 +48,7 @@ class SynEditStringList : public QObject { Q_OBJECT public: - explicit SynEditStringList(); + explicit SynEditStringList(QObject* parent=nullptr); int parenthesisLevels(int Index); int bracketLevels(int Index); @@ -61,13 +61,13 @@ public: void setParenthesisLevel(int Index, int level); void setBracketLevel(int Index, int level); void setBraceLevel(int Index, int level); - QString get(int Index); + QString getString(int Index); int count(); void* getObject(int Index); QString text(); void setText(const QString& text); - void put(int Index, const QString& s); + void putString(int Index, const QString& s); void putObject(int Index, void * AObject); void beginUpdate(); @@ -167,7 +167,8 @@ public: }; using PSynEditUndoItem = std::shared_ptr; -class SynEditUndoList { +class SynEditUndoList : public QObject { + Q_OBJECT public: explicit SynEditUndoList(); @@ -222,4 +223,6 @@ protected: void EnsureMaxEntries(); }; +using PSynEditUndoList = std::shared_ptr; + #endif // SYNEDITSTRINGLIST_H diff --git a/RedPandaIDE/qsynedit/TextDrawer.cpp b/RedPandaIDE/qsynedit/TextDrawer.cpp new file mode 100644 index 00000000..3e6a63b5 --- /dev/null +++ b/RedPandaIDE/qsynedit/TextDrawer.cpp @@ -0,0 +1,2 @@ +#include "TextDrawer.h" + diff --git a/RedPandaIDE/qsynedit/TextDrawer.h b/RedPandaIDE/qsynedit/TextDrawer.h new file mode 100644 index 00000000..c70cb892 --- /dev/null +++ b/RedPandaIDE/qsynedit/TextDrawer.h @@ -0,0 +1,6 @@ +#ifndef SYNTEXTDRAWER_H +#define SYNTEXTDRAWER_H + +#include "Types.h" + +#endif // SYNTEXTDRAWER_H diff --git a/RedPandaIDE/qsynedit/Types.h b/RedPandaIDE/qsynedit/Types.h index 43dbeed9..ac0189f5 100644 --- a/RedPandaIDE/qsynedit/Types.h +++ b/RedPandaIDE/qsynedit/Types.h @@ -1,5 +1,10 @@ #ifndef TYPES_H #define TYPES_H + +#include +#include +#include + enum class SynSelectionMode {smNormal, smLine, smColumn}; struct BufferCoord { @@ -11,4 +16,20 @@ struct DisplayCoord { int Column; int Row; }; + +enum SynFontStyle { + fsBold = 0x0001, + fsItalic = 0x0002, + fsUnderline = 0x0004, + fsStrikeOut = 0x0008 +}; + +Q_DECLARE_FLAGS(SynFontStyles,SynFontStyle) + +Q_DECLARE_OPERATORS_FOR_FLAGS(SynFontStyles) + +using PSynIcon = std::shared_ptr; +using SynIconList = QList; +using PSynIconList = std::shared_ptr; + #endif // TYPES_H diff --git a/RedPandaIDE/qsynedit/highlighter/base.cpp b/RedPandaIDE/qsynedit/highlighter/base.cpp index 338f36d5..a09200c9 100644 --- a/RedPandaIDE/qsynedit/highlighter/base.cpp +++ b/RedPandaIDE/qsynedit/highlighter/base.cpp @@ -166,51 +166,15 @@ void SynHighlighterAttribute::setChanged() emit changed(); } -bool SynHighlighterAttribute::strikeOut() const +SynFontStyles SynHighlighterAttribute::styles() const { - return mStrikeOut; + return mStyles; } -void SynHighlighterAttribute::setStrikeOut(bool strikeOut) +void SynHighlighterAttribute::setStyles(const SynFontStyles &styles) { - if (mStrikeOut!=strikeOut) { - mStrikeOut = strikeOut; - setChanged(); - } -} - -bool SynHighlighterAttribute::underline() const -{ - return mUnderline; -} - -void SynHighlighterAttribute::setUnderline(bool underline) -{ - mUnderline = underline; -} - -bool SynHighlighterAttribute::italic() const -{ - return mItalic; -} - -void SynHighlighterAttribute::setItalic(bool italic) -{ - if (mItalic!=italic) { - mItalic = italic; - setChanged(); - } -} - -bool SynHighlighterAttribute::bold() const -{ - return mBold; -} - -void SynHighlighterAttribute::setBold(bool bold) -{ - if (mBold!=bold) { - mBold = bold; + if (mStyles!=styles) { + mStyles = styles; setChanged(); } } diff --git a/RedPandaIDE/qsynedit/highlighter/base.h b/RedPandaIDE/qsynedit/highlighter/base.h index 6335b8dc..a799a0d5 100644 --- a/RedPandaIDE/qsynedit/highlighter/base.h +++ b/RedPandaIDE/qsynedit/highlighter/base.h @@ -8,6 +8,7 @@ #include #include #include +#include "../Types.h" typedef struct { int state; @@ -40,17 +41,8 @@ public: QString name() const; void setName(const QString &name); - bool bold() const; - void setBold(bool bold); - - bool italic() const; - void setItalic(bool italic); - - bool underline() const; - void setUnderline(bool underline); - - bool strikeOut() const; - void setStrikeOut(bool strikeOut); + SynFontStyles styles() const; + void setStyles(const SynFontStyles &styles); signals: void changed(); @@ -60,10 +52,7 @@ private: QColor mBackground; QColor mForeground; QString mName; - bool mBold; - bool mItalic; - bool mUnderline; - bool mStrikeOut; + SynFontStyles mStyles; }; typedef std::shared_ptr PSynHighlighterAttribute; diff --git a/RedPandaIDE/utils.cpp b/RedPandaIDE/utils.cpp index 65ef7e09..3208cd4c 100644 --- a/RedPandaIDE/utils.cpp +++ b/RedPandaIDE/utils.cpp @@ -329,3 +329,24 @@ FileError::FileError(const QString &reason): BaseError(reason) { } + +void decodeKey(const int combinedKey, int &key, Qt::KeyboardModifiers &modifiers) +{ + modifiers = Qt::NoModifier; + if (combinedKey & Qt::ShiftModifier) { + modifiers|=Qt::ShiftModifier; + } + if (combinedKey & Qt::ControlModifier) { + modifiers|=Qt::ControlModifier; + } + if (combinedKey & Qt::AltModifier) { + modifiers|=Qt::AltModifier; + } + if (combinedKey & Qt::MetaModifier) { + modifiers|=Qt::MetaModifier; + } + if (combinedKey & Qt::KeypadModifier) { + modifiers|= Qt::KeypadModifier; + } + key = combinedKey & ~(Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier | Qt::KeypadModifier); +} diff --git a/RedPandaIDE/utils.h b/RedPandaIDE/utils.h index 68da1ba3..97312b53 100644 --- a/RedPandaIDE/utils.h +++ b/RedPandaIDE/utils.h @@ -90,6 +90,8 @@ QStringList ReadFileToLines(const QString& fileName, QTextCodec* codec); void ReadFileToLines(const QString& fileName, QTextCodec* codec, LineProcessFunc lineFunc); +void decodeKey(int combinedKey, int& key, Qt::KeyboardModifiers& modifiers); + template class final_action {