|
@@ -1,6 +1,6 @@
|
|
|
#include "fb2code.hpp"
|
|
|
|
|
|
-#ifdef USE_SCINTILLA
|
|
|
+#ifdef FB2_USE_SCINTILLA
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
//
|
|
@@ -86,22 +86,363 @@ void Fb2CodeEdit::load(const QByteArray &array, const QList<int> &folds)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-#else // USE_SCINTILLA
|
|
|
+void Fb2CodeEdit::zoomReset()
|
|
|
+{
|
|
|
+ if (codeEdit) codeEdit->zoomTo(1);
|
|
|
+}
|
|
|
+
|
|
|
+#endif // FB2_USE_SCINTILLA
|
|
|
+
|
|
|
+#ifdef FB2_USE_PLAINTEXT
|
|
|
|
|
|
#include <QtGui>
|
|
|
|
|
|
+static const QColor DEFAULT_SYNTAX_CHAR = Qt::blue;
|
|
|
+static const QColor DEFAULT_ELEMENT_NAME = Qt::darkRed;
|
|
|
+static const QColor DEFAULT_COMMENT = Qt::darkGreen;
|
|
|
+static const QColor DEFAULT_ATTRIBUTE_NAME = Qt::red;
|
|
|
+static const QColor DEFAULT_ATTRIBUTE_VALUE = Qt::darkGreen;
|
|
|
+static const QColor DEFAULT_ERROR = Qt::darkMagenta;
|
|
|
+static const QColor DEFAULT_OTHER = Qt::black;
|
|
|
+
|
|
|
+// Regular expressions for parsing XML borrowed from:
|
|
|
+// http://www.cs.sfu.ca/~cameron/REX.html
|
|
|
+static const QString EXPR_COMMENT = "<!--[^-]*-([^-][^-]*-)*->";
|
|
|
+static const QString EXPR_COMMENT_BEGIN = "<!--";
|
|
|
+static const QString EXPR_COMMENT_END = "[^-]*-([^-][^-]*-)*->";
|
|
|
+static const QString EXPR_ATTRIBUTE_VALUE = "\"[^<\"]*\"|'[^<']*'";
|
|
|
+static const QString EXPR_NAME = "([A-Za-z_:]|[^\\x00-\\x7F])([A-Za-z0-9_:.-]|[^\\x00-\\x7F])*";
|
|
|
+
|
|
|
+Fb2Highlighter::Fb2Highlighter(QObject* parent)
|
|
|
+: QSyntaxHighlighter(parent)
|
|
|
+{
|
|
|
+ init();
|
|
|
+}
|
|
|
+
|
|
|
+Fb2Highlighter::Fb2Highlighter(QTextDocument* parent)
|
|
|
+: QSyntaxHighlighter(parent)
|
|
|
+{
|
|
|
+ init();
|
|
|
+}
|
|
|
+
|
|
|
+Fb2Highlighter::Fb2Highlighter(QTextEdit* parent)
|
|
|
+: QSyntaxHighlighter(parent)
|
|
|
+{
|
|
|
+ init();
|
|
|
+}
|
|
|
+
|
|
|
+Fb2Highlighter::~Fb2Highlighter()
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+void Fb2Highlighter::init()
|
|
|
+{
|
|
|
+ fmtSyntaxChar.setForeground(DEFAULT_SYNTAX_CHAR);
|
|
|
+ fmtElementName.setForeground(DEFAULT_ELEMENT_NAME);
|
|
|
+ fmtComment.setForeground(DEFAULT_COMMENT);
|
|
|
+ fmtAttributeName.setForeground(DEFAULT_ATTRIBUTE_NAME);
|
|
|
+ fmtAttributeValue.setForeground(DEFAULT_ATTRIBUTE_VALUE);
|
|
|
+ fmtError.setForeground(DEFAULT_ERROR);
|
|
|
+ fmtOther.setForeground(DEFAULT_OTHER);
|
|
|
+}
|
|
|
+
|
|
|
+void Fb2Highlighter::setHighlightColor(HighlightType type, QColor color, bool foreground)
|
|
|
+{
|
|
|
+ QTextCharFormat format;
|
|
|
+ if (foreground)
|
|
|
+ format.setForeground(color);
|
|
|
+ else
|
|
|
+ format.setBackground(color);
|
|
|
+ setHighlightFormat(type, format);
|
|
|
+}
|
|
|
+
|
|
|
+void Fb2Highlighter::setHighlightFormat(HighlightType type, QTextCharFormat format)
|
|
|
+{
|
|
|
+ switch (type)
|
|
|
+ {
|
|
|
+ case SyntaxChar:
|
|
|
+ fmtSyntaxChar = format;
|
|
|
+ break;
|
|
|
+ case ElementName:
|
|
|
+ fmtElementName = format;
|
|
|
+ break;
|
|
|
+ case Comment:
|
|
|
+ fmtComment = format;
|
|
|
+ break;
|
|
|
+ case AttributeName:
|
|
|
+ fmtAttributeName = format;
|
|
|
+ break;
|
|
|
+ case AttributeValue:
|
|
|
+ fmtAttributeValue = format;
|
|
|
+ break;
|
|
|
+ case Error:
|
|
|
+ fmtError = format;
|
|
|
+ break;
|
|
|
+ case Other:
|
|
|
+ fmtOther = format;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ rehighlight();
|
|
|
+}
|
|
|
+
|
|
|
+void Fb2Highlighter::highlightBlock(const QString& text)
|
|
|
+{
|
|
|
+ int i = 0;
|
|
|
+ int pos = 0;
|
|
|
+ int brackets = 0;
|
|
|
+
|
|
|
+ state = (previousBlockState() == InElement ? ExpectAttributeOrEndOfElement : NoState);
|
|
|
+
|
|
|
+ if (previousBlockState() == InComment)
|
|
|
+ {
|
|
|
+ // search for the end of the comment
|
|
|
+ QRegExp expression(EXPR_COMMENT_END);
|
|
|
+ pos = expression.indexIn(text, i);
|
|
|
+
|
|
|
+ if (pos >= 0)
|
|
|
+ {
|
|
|
+ // end comment found
|
|
|
+ const int iLength = expression.matchedLength();
|
|
|
+ setFormat(0, iLength - 3, fmtComment);
|
|
|
+ setFormat(iLength - 3, 3, fmtSyntaxChar);
|
|
|
+ i += iLength; // skip comment
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // in comment
|
|
|
+ setFormat(0, text.length(), fmtComment);
|
|
|
+ setCurrentBlockState(InComment);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (; i < text.length(); i++)
|
|
|
+ {
|
|
|
+ switch (text.at(i).toAscii())
|
|
|
+ {
|
|
|
+ case '<':
|
|
|
+ brackets++;
|
|
|
+ if (brackets == 1)
|
|
|
+ {
|
|
|
+ setFormat(i, 1, fmtSyntaxChar);
|
|
|
+ state = ExpectElementNameOrSlash;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // wrong bracket nesting
|
|
|
+ setFormat(i, 1, fmtError);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case '>':
|
|
|
+ brackets--;
|
|
|
+ if (brackets == 0)
|
|
|
+ {
|
|
|
+ setFormat(i, 1, fmtSyntaxChar);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // wrong bracket nesting
|
|
|
+ setFormat( i, 1, fmtError);
|
|
|
+ }
|
|
|
+ state = NoState;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case '/':
|
|
|
+ if (state == ExpectElementNameOrSlash)
|
|
|
+ {
|
|
|
+ state = ExpectElementName;
|
|
|
+ setFormat(i, 1, fmtSyntaxChar);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (state == ExpectAttributeOrEndOfElement)
|
|
|
+ {
|
|
|
+ setFormat(i, 1, fmtSyntaxChar);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ processDefaultText(i, text);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case '=':
|
|
|
+ if (state == ExpectEqual)
|
|
|
+ {
|
|
|
+ state = ExpectAttributeValue;
|
|
|
+ setFormat(i, 1, fmtOther);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ processDefaultText(i, text);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case '\'':
|
|
|
+ case '\"':
|
|
|
+ if (state == ExpectAttributeValue)
|
|
|
+ {
|
|
|
+ // search attribute value
|
|
|
+ QRegExp expression(EXPR_ATTRIBUTE_VALUE);
|
|
|
+ pos = expression.indexIn(text, i);
|
|
|
+
|
|
|
+ if (pos == i) // attribute value found ?
|
|
|
+ {
|
|
|
+ const int iLength = expression.matchedLength();
|
|
|
+
|
|
|
+ setFormat(i, 1, fmtOther);
|
|
|
+ setFormat(i + 1, iLength - 2, fmtAttributeValue);
|
|
|
+ setFormat(i + iLength - 1, 1, fmtOther);
|
|
|
+
|
|
|
+ i += iLength - 1; // skip attribute value
|
|
|
+ state = ExpectAttributeOrEndOfElement;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ processDefaultText(i, text);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ processDefaultText(i, text);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case '!':
|
|
|
+ if (state == ExpectElementNameOrSlash)
|
|
|
+ {
|
|
|
+ // search comment
|
|
|
+ QRegExp expression(EXPR_COMMENT);
|
|
|
+ pos = expression.indexIn(text, i - 1);
|
|
|
+
|
|
|
+ if (pos == i - 1) // comment found ?
|
|
|
+ {
|
|
|
+ const int iLength = expression.matchedLength();
|
|
|
+
|
|
|
+ setFormat(pos, 4, fmtSyntaxChar);
|
|
|
+ setFormat(pos + 4, iLength - 7, fmtComment);
|
|
|
+ setFormat(iLength - 3, 3, fmtSyntaxChar);
|
|
|
+ i += iLength - 2; // skip comment
|
|
|
+ state = NoState;
|
|
|
+ brackets--;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Try find multiline comment
|
|
|
+ QRegExp expression(EXPR_COMMENT_BEGIN); // search comment start
|
|
|
+ pos = expression.indexIn(text, i - 1);
|
|
|
+
|
|
|
+ //if (pos == i - 1) // comment found ?
|
|
|
+ if (pos >= i - 1)
|
|
|
+ {
|
|
|
+ setFormat(i, 3, fmtSyntaxChar);
|
|
|
+ setFormat(i + 3, text.length() - i - 3, fmtComment);
|
|
|
+ setCurrentBlockState(InComment);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ processDefaultText(i, text);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ processDefaultText(i, text);
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ const int iLength = processDefaultText(i, text);
|
|
|
+ if (iLength > 0)
|
|
|
+ i += iLength - 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (state == ExpectAttributeOrEndOfElement)
|
|
|
+ {
|
|
|
+ setCurrentBlockState(InElement);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int Fb2Highlighter::processDefaultText(int i, const QString& text)
|
|
|
+{
|
|
|
+ // length of matched text
|
|
|
+ int iLength = 0;
|
|
|
+
|
|
|
+ switch(state)
|
|
|
+ {
|
|
|
+ case ExpectElementNameOrSlash:
|
|
|
+ case ExpectElementName:
|
|
|
+ {
|
|
|
+ // search element name
|
|
|
+ QRegExp expression(EXPR_NAME);
|
|
|
+ const int pos = expression.indexIn(text, i);
|
|
|
+
|
|
|
+ if (pos == i) // found ?
|
|
|
+ {
|
|
|
+ iLength = expression.matchedLength();
|
|
|
+
|
|
|
+ setFormat(pos, iLength, fmtElementName);
|
|
|
+ state = ExpectAttributeOrEndOfElement;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ setFormat(i, 1, fmtOther);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case ExpectAttributeOrEndOfElement:
|
|
|
+ {
|
|
|
+ // search attribute name
|
|
|
+ QRegExp expression(EXPR_NAME);
|
|
|
+ const int pos = expression.indexIn(text, i);
|
|
|
+
|
|
|
+ if (pos == i) // found ?
|
|
|
+ {
|
|
|
+ iLength = expression.matchedLength();
|
|
|
+
|
|
|
+ setFormat(pos, iLength, fmtAttributeName);
|
|
|
+ state = ExpectEqual;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ setFormat(i, 1, fmtOther);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ setFormat(i, 1, fmtOther);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return iLength;
|
|
|
+}
|
|
|
+
|
|
|
+qreal Fb2CodeEdit::baseFontSize = 10;
|
|
|
+qreal Fb2CodeEdit::zoomRatioMin = 0.2;
|
|
|
+qreal Fb2CodeEdit::zoomRatioMax = 5.0;
|
|
|
+
|
|
|
Fb2CodeEdit::Fb2CodeEdit(QWidget *parent) : QPlainTextEdit(parent)
|
|
|
{
|
|
|
lineNumberArea = new LineNumberArea(this);
|
|
|
+ highlighter = new Fb2Highlighter(this);
|
|
|
+ highlighter->setDocument( document() );
|
|
|
|
|
|
connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
|
|
|
connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int)));
|
|
|
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine()));
|
|
|
|
|
|
+ zoomRatio = 1;
|
|
|
+
|
|
|
#ifdef Q_WS_WIN
|
|
|
- setFont(QFont("Courier New", 8));
|
|
|
+ setFont(QFont("Courier New", baseFontSize));
|
|
|
#else
|
|
|
- setFont(QFont("Monospace", 8));
|
|
|
+ setFont(QFont("Monospace", baseFontSize));
|
|
|
#endif
|
|
|
|
|
|
updateLineNumberAreaWidth(0);
|
|
@@ -190,4 +531,34 @@ void Fb2CodeEdit::lineNumberAreaPaintEvent(QPaintEvent *event)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-#endif // USE_SCINTILLA
|
|
|
+void Fb2CodeEdit::zoomIn()
|
|
|
+{
|
|
|
+ qreal ratio = zoomRatio * 1.1;
|
|
|
+ ratio = qMin(ratio, zoomRatioMax);
|
|
|
+ setZoomRatio(ratio);
|
|
|
+}
|
|
|
+
|
|
|
+void Fb2CodeEdit::zoomOut()
|
|
|
+{
|
|
|
+ qreal ratio = zoomRatio / 1.1;
|
|
|
+ ratio = qMax(ratio, zoomRatioMin);
|
|
|
+ setZoomRatio(ratio);
|
|
|
+}
|
|
|
+
|
|
|
+void Fb2CodeEdit::zoomReset()
|
|
|
+{
|
|
|
+ setZoomRatio(1.0);
|
|
|
+}
|
|
|
+
|
|
|
+void Fb2CodeEdit::setZoomRatio(qreal ratio)
|
|
|
+{
|
|
|
+ if (!qFuzzyCompare(1 + zoomRatio, 1 + ratio)) {
|
|
|
+ zoomRatio = ratio;
|
|
|
+ QFont f = font();
|
|
|
+ f.setPointSizeF(baseFontSize * zoomRatio);
|
|
|
+ setFont(f);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#endif // FB2_USE_PLAINTEXT
|