822 lines
24 KiB
C++
822 lines
24 KiB
C++
#include "qcodeedit.h"
|
|
#include <QLayout>
|
|
#include <QBoxLayout>
|
|
#include <QScrollBar>
|
|
#include <QTextDocument>
|
|
#include <QTextDocumentFragment>
|
|
#include <QTextOption>
|
|
#include <QTextCursor>
|
|
#include <QTextBlock>
|
|
#include <QAction>
|
|
#include <QApplication>
|
|
#include <QHeaderView>
|
|
|
|
|
|
QCodeEdit::QCodeEdit(QWidget * parent): QWidget(parent) {
|
|
prev_lc = auto_comp_pl = -1;
|
|
textCode = textLines = 0;
|
|
|
|
timer = 0;
|
|
_ignore_focus_out = false;
|
|
_first = true;
|
|
es_line.format.setBackground(QColor(240, 245, 240));
|
|
es_line.format.setProperty(QTextFormat::FullWidthSelection, true);
|
|
completer.setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
|
|
completer.setFocusPolicy(Qt::NoFocus);
|
|
completer.setColumnCount(2);
|
|
completer.setRootIsDecorated(false);
|
|
completer.setHeaderHidden(true);
|
|
completer.header()->setDefaultAlignment(Qt::AlignCenter);
|
|
completer.header()->
|
|
#if (QT_VERSION >= 0x050000)
|
|
setSectionResizeMode
|
|
#else
|
|
setResizeMode
|
|
#endif
|
|
(QHeaderView::ResizeToContents);
|
|
completer.header()->setStretchLastSection(true);
|
|
//completer.setColumnWidth(0, 180);
|
|
completer.resize(500, 200);
|
|
textCode = new QPlainTextEdit();
|
|
textLines = new QPlainTextEdit();
|
|
textCode->setFrameShadow(QFrame::Plain);
|
|
textCode->setFrameShape(QFrame::NoFrame);
|
|
textCode->setLineWrapMode(QPlainTextEdit::NoWrap);
|
|
textCode->setTabChangesFocus(false);
|
|
textLines->setFrameShadow(QFrame::Plain);
|
|
textLines->setFrameShape(QFrame::NoFrame);
|
|
textLines->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
|
textLines->setFocusPolicy(Qt::NoFocus);
|
|
textLines->setTextInteractionFlags(Qt::NoTextInteraction);
|
|
textLines->setLineWrapMode(QPlainTextEdit::NoWrap);
|
|
textLines->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
textLines->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
textLines->viewport()->setAutoFillBackground(false);
|
|
textLines->viewport()->setCursor(Qt::ArrowCursor);
|
|
textLines->setFixedWidth(textLines->fontMetrics().width(" "));
|
|
setLayout(new QBoxLayout(QBoxLayout::BottomToTop));
|
|
layout()->setContentsMargins(0, 0, 0, 0);
|
|
QFrame * frame = new QFrame();
|
|
frame->setFrameShadow(QFrame::Sunken);
|
|
frame->setFrameShape(QFrame::StyledPanel);
|
|
frame->setLayout(new QBoxLayout(QBoxLayout::LeftToRight));
|
|
frame->layout()->setContentsMargins(0, 0, 0, 0);
|
|
frame->layout()->setSpacing(0);
|
|
frame->layout()->addWidget(textLines);
|
|
frame->layout()->addWidget(textCode);
|
|
layout()->addWidget(frame);
|
|
|
|
QAction * a = new QAction(this);
|
|
a->setShortcut(QKeySequence("Shift+Tab"));
|
|
a->setShortcutContext(Qt::WidgetShortcut);
|
|
connect(a, SIGNAL(triggered(bool)), this, SLOT(deindent()));
|
|
textCode->addAction(a);
|
|
a = new QAction(this);
|
|
a->setShortcut(QKeySequence("Ctrl+D"));
|
|
a->setShortcutContext(Qt::WidgetShortcut);
|
|
connect(a, SIGNAL(triggered(bool)), this, SLOT(deleteLine()));
|
|
textCode->addAction(a);
|
|
frame->setFocusProxy(textCode);
|
|
QTextOption to = textLines->document()->defaultTextOption();
|
|
to.setAlignment(Qt::AlignTop | Qt::AlignRight);
|
|
textLines->document()->setDefaultTextOption(to);
|
|
setShowSpaces(true);
|
|
|
|
connect(&completer, SIGNAL(itemDoubleClicked(QTreeWidgetItem * ,int)), this, SLOT(commitCompletition()));
|
|
connect(textCode->verticalScrollBar(), SIGNAL(valueChanged(int)), textLines->verticalScrollBar(), SLOT(setValue(int)));
|
|
connect(textCode, SIGNAL(textChanged()), this, SLOT(updateLines()));
|
|
connect(textCode, SIGNAL(textChanged()), this, SIGNAL(textChanged()));
|
|
connect(textCode, SIGNAL(cursorPositionChanged()), this, SLOT(textEdit_cursorPositionChanged()));
|
|
connect(textCode, SIGNAL(selectionChanged()), this, SLOT(textEdit_selectionChanged()));
|
|
updateLines();
|
|
|
|
registerAutoCompletitionClass(-1, QCodeEdit::Keyword, "Words", QIcon(":/icons/code-word.png"));
|
|
}
|
|
|
|
|
|
QCodeEdit::~QCodeEdit() {
|
|
delete textCode;
|
|
delete textLines;
|
|
}
|
|
|
|
|
|
int QCodeEdit::skipRange(const QString & s, int pos, QChar oc, QChar cc, QChar sc) {
|
|
int cnt = 0;
|
|
bool skip = false;
|
|
for (int i = pos - 1; i >= 0; --i) {
|
|
QChar c = s[i];
|
|
if (skip) {skip = false; continue;}
|
|
if (c == sc) {skip = true; continue;}
|
|
if (c == cc) {cnt++; continue;}
|
|
if (c == oc) {cnt--; if (cnt == 0) return i;}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
int QCodeEdit::skipCWord(const QString & s, int pos) {
|
|
QChar pc(0), c(0);
|
|
for (int i = pos - 1; i >= 0; --i) {
|
|
pc = c;
|
|
c = s[i];
|
|
if (c.isLetterOrNumber() || (c.toLatin1() == '_')) continue;
|
|
if (pc.isLetter() || (pc.toLatin1() == '_')) return i + 1;
|
|
return -1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
bool QCodeEdit::matchWritten(QString s, QString w) {
|
|
if (s.isEmpty() || w.isEmpty()) return true;
|
|
if (s.contains(w, Qt::CaseInsensitive)) return true;
|
|
int sp(0);
|
|
for (int i = 0; i < w.size(); ++i, ++sp) {
|
|
if (sp >= s.size()) return false;
|
|
QChar wc(w[i].toLower());
|
|
bool ns = false, bl = true;
|
|
while (sp < s.size()) {
|
|
if (ns || s[sp].toLatin1() == '_') {
|
|
if (s[sp].toLatin1() == '_') {sp++; bl = false; continue;}
|
|
if (s[sp].isLower() && bl) {sp++; continue;}
|
|
if (s[sp].toLower() != wc) return false;
|
|
}
|
|
if (s[sp].toLower() == wc) break;
|
|
ns = true;
|
|
sp++;
|
|
}
|
|
if (sp >= s.size()) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
QChar QCodeEdit::pairChar(QChar c) {
|
|
switch (c.toLatin1()) {
|
|
case '\"': return '\"';
|
|
case '(': return ')';
|
|
case ')': return '(';
|
|
case '[': return ']';
|
|
case ']': return '[';
|
|
default: break;
|
|
}
|
|
return QChar();
|
|
}
|
|
|
|
|
|
bool QCodeEdit::eventFilter(QObject * o, QEvent * e) {
|
|
if (textLines) {
|
|
if (o == textLines->viewport()) {
|
|
if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonRelease ||
|
|
e->type() == QEvent::MouseMove || e->type() == QEvent::MouseButtonDblClick) {
|
|
const_cast<QPoint&>(((QMouseEvent*)e)->pos()) = QPoint(0, ((QMouseEvent*)e)->pos().y());
|
|
QApplication::sendEvent(textCode->viewport(), e);
|
|
return true;
|
|
}
|
|
if (e->type() == QEvent::Wheel) {
|
|
QApplication::sendEvent(textCode->viewport(), e);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
if (o == &completer) {
|
|
//qDebug() << o << e;
|
|
if (e->type() == QEvent::WindowActivate)
|
|
_ignore_focus_out = true;
|
|
//qDebug() << e;
|
|
return QWidget::eventFilter(o, e);
|
|
}
|
|
if (textCode) {
|
|
if (o == textCode->viewport()) {
|
|
if (e->type() == QEvent::MouseButtonPress)
|
|
completer.hide();
|
|
return QWidget::eventFilter(o, e);
|
|
}
|
|
if (o == textCode) {
|
|
//qDebug() << o << e;
|
|
QKeyEvent * ke;
|
|
QChar kc(0);
|
|
switch (e->type()) {
|
|
case QEvent::KeyPress:
|
|
ke = (QKeyEvent * )e;
|
|
switch (ke->key()) {
|
|
case Qt::Key_Space:
|
|
if (ke->modifiers().testFlag(Qt::ControlModifier)) {
|
|
invokeAutoCompletition(true);
|
|
return true;
|
|
}
|
|
break;
|
|
case Qt::Key_Escape:
|
|
completer.hide();
|
|
break;
|
|
case Qt::Key_Up:
|
|
if (completer.isVisible()) {
|
|
previousCompletition();
|
|
return true;
|
|
}
|
|
completer.hide();
|
|
if (ke->modifiers().testFlag(Qt::AltModifier)) {
|
|
copyLineUp();
|
|
return true;
|
|
}
|
|
if (ke->modifiers().testFlag(Qt::ControlModifier) && ke->modifiers().testFlag(Qt::ShiftModifier)) {
|
|
moveLineUp();
|
|
return true;
|
|
}
|
|
break;
|
|
case Qt::Key_Down:
|
|
if (completer.isVisible()) {
|
|
nextCompletition();
|
|
return true;
|
|
}
|
|
completer.hide();
|
|
if (ke->modifiers().testFlag(Qt::AltModifier)) {
|
|
copyLineDown();
|
|
return true;
|
|
}
|
|
if (ke->modifiers().testFlag(Qt::ControlModifier) && ke->modifiers().testFlag(Qt::ShiftModifier)) {
|
|
moveLineDown();
|
|
return true;
|
|
}
|
|
break;
|
|
case Qt::Key_Home:
|
|
case Qt::Key_End:
|
|
case Qt::Key_PageUp:
|
|
case Qt::Key_PageDown:
|
|
if (completer.isVisible()) {
|
|
qApp->sendEvent(&completer, new QKeyEvent(e->type(), ke->key(), ke->modifiers()));
|
|
return true;
|
|
}
|
|
break;
|
|
case Qt::Key_Left:
|
|
case Qt::Key_Right:
|
|
case Qt::Key_Backspace:
|
|
case Qt::Key_Delete:
|
|
if (completer.isVisible())
|
|
QMetaObject::invokeMethod(this, "invokeAutoCompletition", Qt::QueuedConnection, Q_ARG(bool, false));
|
|
break;
|
|
case Qt::Key_Return:
|
|
if (completer.isVisible()) {
|
|
commitCompletition();
|
|
completer.hide();
|
|
return true;
|
|
}
|
|
if (textCode->textCursor().selectedText().isEmpty())
|
|
QMetaObject::invokeMethod(this, "autoIndent", Qt::QueuedConnection);
|
|
break;
|
|
case Qt::Key_Tab:
|
|
if (!textCode->textCursor().selectedText().isEmpty()) {
|
|
if (ke->modifiers().testFlag(Qt::ShiftModifier))
|
|
deindent();
|
|
else
|
|
indent();
|
|
return true;
|
|
}
|
|
break;
|
|
case Qt::Key_D:
|
|
if (ke->modifiers().testFlag(Qt::ControlModifier)) {
|
|
completer.hide();
|
|
return true;
|
|
}
|
|
break;
|
|
default: break;
|
|
}
|
|
if (!ke->text().isEmpty())
|
|
kc = ke->text()[0];
|
|
if (kc == '.') {
|
|
completer.hide();
|
|
QMetaObject::invokeMethod(this, "invokeAutoCompletition", Qt::QueuedConnection, Q_ARG(bool, false));
|
|
} else {
|
|
if ((kc.isLetterOrNumber() || kc.toLatin1() == '_') && completer.isVisible())
|
|
QMetaObject::invokeMethod(this, "invokeAutoCompletition", Qt::QueuedConnection, Q_ARG(bool, false));
|
|
}
|
|
break;
|
|
case QEvent::FocusOut:
|
|
if (_ignore_focus_out) {
|
|
_ignore_focus_out = false;
|
|
break;
|
|
}
|
|
case QEvent::Hide:
|
|
case QEvent::HideToParent:
|
|
case QEvent::MouseButtonPress:
|
|
//qDebug() << e;
|
|
completer.hide();
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
return QWidget::eventFilter(o, e);
|
|
}
|
|
|
|
|
|
void QCodeEdit::showEvent(QShowEvent * ) {
|
|
if (!_first) return;
|
|
_first = false;
|
|
completer.installEventFilter(this);
|
|
textCode->installEventFilter(this);
|
|
textCode->viewport()->installEventFilter(this);
|
|
textLines->viewport()->installEventFilter(this);
|
|
}
|
|
|
|
|
|
void QCodeEdit::timerEvent(QTimerEvent * ) {
|
|
parse();
|
|
emit parseRequest();
|
|
killTimer(timer);
|
|
timer = 0;
|
|
}
|
|
|
|
|
|
void QCodeEdit::applyExtraSelection() {
|
|
textCode->setExtraSelections(QList<QTextEdit::ExtraSelection>() << es_line << es_selected << es_custom);
|
|
}
|
|
|
|
|
|
void QCodeEdit::nextCompletition() {
|
|
int ci = completer.currentIndex().row();
|
|
if (ci >= completer.topLevelItemCount() - 1) return;
|
|
if (completer.topLevelItem(ci + 1)->flags().testFlag(Qt::ItemIsSelectable))
|
|
completer.setCurrentItem(completer.topLevelItem(ci + 1));
|
|
else {
|
|
if (ci >= completer.topLevelItemCount() - 2) return;
|
|
completer.setCurrentItem(completer.topLevelItem(ci + 2));
|
|
}
|
|
}
|
|
|
|
|
|
void QCodeEdit::previousCompletition() {
|
|
int ci = completer.currentIndex().row();
|
|
if (ci <= 0) return;
|
|
if (completer.topLevelItem(ci - 1)->flags().testFlag(Qt::ItemIsSelectable))
|
|
completer.setCurrentItem(completer.topLevelItem(ci - 1));
|
|
else {
|
|
if (ci <= 1) return;
|
|
completer.setCurrentItem(completer.topLevelItem(ci - 2));
|
|
}
|
|
}
|
|
|
|
|
|
void QCodeEdit::deleteLine() {
|
|
QTextCursor tc = textCode->textCursor();
|
|
tc.movePosition(QTextCursor::EndOfLine);
|
|
tc.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
|
|
bool md = true;
|
|
if (!tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor)) {
|
|
tc.movePosition(QTextCursor::StartOfLine);
|
|
tc.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
|
|
tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
|
|
md = false;
|
|
}
|
|
tc.removeSelectedText();
|
|
tc.movePosition(QTextCursor::StartOfLine);
|
|
if (md) tc.movePosition(QTextCursor::Down);
|
|
textCode->setTextCursor(tc);
|
|
}
|
|
|
|
|
|
void QCodeEdit::copyLineUp() {
|
|
QTextCursor tc = textCode->textCursor();
|
|
int ss = tc.selectionStart(), ss_ = ss, se = tc.selectionEnd(), se_ = se;
|
|
QString st_ = tc.selection().toPlainText();
|
|
if (st_.endsWith("\n")) {
|
|
st_.chop(1);
|
|
se--; se_--;
|
|
}
|
|
tc.setPosition(ss); tc.movePosition(QTextCursor::StartOfLine); ss = tc.position();
|
|
tc.setPosition(se); tc.movePosition(QTextCursor::EndOfLine); se = tc.position();
|
|
tc.setPosition(ss); tc.setPosition(se, QTextCursor::KeepAnchor);
|
|
bool ins_nl = false;
|
|
if (!tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor))
|
|
ins_nl = true;
|
|
QString l = tc.selectedText();
|
|
tc.beginEditBlock();
|
|
tc.setPosition(ss);
|
|
if (ins_nl)
|
|
l.append("\n");
|
|
tc.insertText(l);
|
|
tc.setPosition(ss_);
|
|
tc.setPosition(se_, QTextCursor::KeepAnchor);
|
|
tc.endEditBlock();
|
|
textCode->setTextCursor(tc);
|
|
}
|
|
|
|
|
|
void QCodeEdit::copyLineDown() {
|
|
QTextCursor tc = textCode->textCursor();
|
|
int ss = tc.selectionStart(), ss_ = ss, se = tc.selectionEnd(), se_ = se;
|
|
QString st_ = tc.selection().toPlainText();
|
|
if (st_.endsWith("\n")) {
|
|
st_.chop(1);
|
|
se--; se_--;
|
|
}
|
|
tc.setPosition(ss); tc.movePosition(QTextCursor::StartOfLine); ss = tc.position();
|
|
tc.setPosition(se); tc.movePosition(QTextCursor::EndOfLine); se = tc.position();
|
|
tc.setPosition(ss); tc.setPosition(se, QTextCursor::KeepAnchor);
|
|
bool ins_nl = false;
|
|
if (!tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor))
|
|
ins_nl = true;
|
|
QString l = tc.selectedText();
|
|
tc.beginEditBlock();
|
|
tc.setPosition(ss);
|
|
ss_ += l.size(); se_ += l.size();
|
|
if (ins_nl) {
|
|
l.append("\n");
|
|
ss_++; se_++;
|
|
}
|
|
tc.insertText(l);
|
|
tc.setPosition(ss_);
|
|
tc.setPosition(se_, QTextCursor::KeepAnchor);
|
|
tc.endEditBlock();
|
|
textCode->setTextCursor(tc);
|
|
}
|
|
|
|
|
|
void QCodeEdit::moveLineUp() {
|
|
QTextCursor tc = textCode->textCursor();
|
|
int ss = tc.selectionStart(), ss_ = ss, se = tc.selectionEnd(), se_ = se;
|
|
QString st_ = tc.selection().toPlainText();
|
|
if (st_.endsWith("\n")) {
|
|
st_.chop(1);
|
|
se--; se_--;
|
|
}
|
|
tc.setPosition(ss); tc.movePosition(QTextCursor::StartOfLine); ss = tc.position();
|
|
tc.setPosition(se); tc.movePosition(QTextCursor::EndOfLine); se = tc.position();
|
|
tc.setPosition(ss);
|
|
if (!tc.movePosition(QTextCursor::Up))
|
|
return;
|
|
tc.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor);
|
|
QString l = tc.selectedText();
|
|
ss -= l.size(); se -= l.size();
|
|
ss_ -= l.size(); se_ -= l.size();
|
|
tc.beginEditBlock();
|
|
tc.removeSelectedText();
|
|
tc.setPosition(se);
|
|
bool de = false;
|
|
if (!tc.movePosition(QTextCursor::Right)) {
|
|
l.prepend("\n");
|
|
de = true;
|
|
}
|
|
tc.insertText(l);
|
|
if (de) {
|
|
tc.movePosition(QTextCursor::End);
|
|
tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor);
|
|
tc.removeSelectedText();
|
|
}
|
|
tc.setPosition(ss_);
|
|
tc.setPosition(se_, QTextCursor::KeepAnchor);
|
|
tc.endEditBlock();
|
|
textCode->setTextCursor(tc);
|
|
}
|
|
|
|
|
|
void QCodeEdit::moveLineDown() {
|
|
QTextCursor tc = textCode->textCursor();
|
|
int ss = tc.selectionStart(), ss_ = ss, se = tc.selectionEnd(), se_ = se;
|
|
QString st_ = tc.selection().toPlainText();
|
|
if (st_.endsWith("\n")) {
|
|
st_.chop(1);
|
|
se--; se_--;
|
|
}
|
|
tc.setPosition(ss); tc.movePosition(QTextCursor::StartOfLine); ss = tc.position();
|
|
tc.setPosition(se); tc.movePosition(QTextCursor::EndOfLine); se = tc.position();
|
|
tc.setPosition(se);
|
|
if (!tc.movePosition(QTextCursor::Right))
|
|
return;
|
|
bool de = false;
|
|
if (!tc.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor)) {
|
|
tc.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
|
|
de = true;
|
|
}
|
|
QString l = tc.selectedText();
|
|
tc.beginEditBlock();
|
|
tc.removeSelectedText();
|
|
tc.setPosition(ss);
|
|
if (de) l += "\n";
|
|
tc.insertText(l);
|
|
if (de) {
|
|
tc.movePosition(QTextCursor::End);
|
|
tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor);
|
|
tc.removeSelectedText();
|
|
}
|
|
ss += l.size(); se += l.size();
|
|
ss_ += l.size(); se_ += l.size();
|
|
tc.setPosition(ss_);
|
|
tc.setPosition(se_, QTextCursor::KeepAnchor);
|
|
tc.endEditBlock();
|
|
textCode->setTextCursor(tc);
|
|
}
|
|
|
|
|
|
void QCodeEdit::indent() {
|
|
QTextCursor tc = textCode->textCursor();
|
|
int ss = tc.selectionStart(), ss_ = ss, se = tc.selectionEnd(), se_ = se;
|
|
QString st_ = tc.selection().toPlainText();
|
|
if (st_.endsWith("\n")) {
|
|
st_.chop(1);
|
|
se--; se_--;
|
|
}
|
|
tc.setPosition(ss); tc.movePosition(QTextCursor::StartOfLine); ss = tc.position();
|
|
tc.setPosition(se); tc.movePosition(QTextCursor::EndOfLine); se = tc.position();
|
|
tc.setPosition(ss);
|
|
while (tc.position() < se_) {
|
|
tc.insertText("\t");
|
|
se_++;
|
|
tc.movePosition(QTextCursor::StartOfLine);
|
|
if (!tc.movePosition(QTextCursor::Down))
|
|
break;
|
|
}
|
|
tc.setPosition(ss_ + 1);
|
|
tc.setPosition(se_, QTextCursor::KeepAnchor);
|
|
textCode->setTextCursor(tc);
|
|
}
|
|
|
|
|
|
void QCodeEdit::deindent() {
|
|
QTextCursor tc = textCode->textCursor();
|
|
int ss = tc.selectionStart(), ss_ = ss, se = tc.selectionEnd(), se_ = se;
|
|
QString st_ = tc.selection().toPlainText();
|
|
if (st_.endsWith("\n")) {
|
|
st_.chop(1);
|
|
se--; se_--;
|
|
}
|
|
tc.setPosition(ss); tc.movePosition(QTextCursor::StartOfLine); ss = tc.position();
|
|
tc.setPosition(se); tc.movePosition(QTextCursor::EndOfLine); se = tc.position();
|
|
tc.setPosition(ss);
|
|
bool first = true;
|
|
while (tc.position() < se_) {
|
|
tc.movePosition(QTextCursor::StartOfLine);
|
|
tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
|
|
int rs = 0;
|
|
if (tc.selectedText() == "\t") {
|
|
tc.removeSelectedText();
|
|
rs = 1;
|
|
} else {
|
|
for (int i = 0; i < 4; ++i) {
|
|
tc.movePosition(QTextCursor::StartOfLine);
|
|
tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
|
|
if (tc.selectedText() == " ") {
|
|
tc.removeSelectedText();
|
|
rs++;
|
|
}
|
|
}
|
|
}
|
|
if (first) {
|
|
first = false;
|
|
ss_ -= rs;
|
|
}
|
|
se_ -= rs;
|
|
tc.movePosition(QTextCursor::StartOfLine);
|
|
if (!tc.movePosition(QTextCursor::Down))
|
|
break;
|
|
}
|
|
tc.setPosition(ss_);
|
|
tc.setPosition(se_, QTextCursor::KeepAnchor);
|
|
textCode->setTextCursor(tc);
|
|
}
|
|
|
|
|
|
void QCodeEdit::autoIndent() {
|
|
QTextCursor tc = textCode->textCursor(), stc = tc;
|
|
tc.movePosition(QTextCursor::StartOfLine);
|
|
if (!tc.movePosition(QTextCursor::Up)) return;
|
|
tc.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor);
|
|
QString line = tc.selectedText(), tabs;
|
|
int i = 0;
|
|
for (; i < line.size(); ++i)
|
|
if (!line[i].isSpace()) {
|
|
tabs = line.left(i);
|
|
break;
|
|
}
|
|
if (i >= line.size())
|
|
tabs = line.left(line.size() - 1);
|
|
int nt = qMax<int>(0, line.count(QChar('{')) - line.count(QChar('}')));
|
|
tabs.append(QString("\t").repeated(nt));
|
|
if (tabs.isEmpty()) return;
|
|
stc.insertText(tabs);
|
|
textCode->setTextCursor(stc);
|
|
}
|
|
|
|
|
|
void QCodeEdit::updateLines() {
|
|
if (timer > 0) killTimer(timer);
|
|
timer = startTimer(500);
|
|
textCode->setTabStopWidth(textCode->fontMetrics().width(" "));
|
|
int lc = textCode->document()->lineCount();
|
|
if (prev_lc == lc) return;
|
|
prev_lc = lc;
|
|
textLines->setFixedWidth(textLines->fontMetrics().width(QString(" %1").arg(lc)));
|
|
textLines->clear();
|
|
for (int i = 1; i <= lc; ++i)
|
|
textLines->appendPlainText(QString("%1").arg(i));
|
|
textLines->verticalScrollBar()->setValue(textCode->verticalScrollBar()->value());
|
|
}
|
|
|
|
|
|
QCodeEdit::ACList QCodeEdit::wordsCompletitionList(const QString & written) const {
|
|
QCodeEdit::ACList ret;
|
|
if (!written.isEmpty()) {
|
|
QTextCursor tc = QTextCursor(textCode->document()->begin()), stc;
|
|
QStringList acwl;
|
|
tc = QTextCursor(textCode->document()->begin());
|
|
while (true) {
|
|
tc = textCode->document()->find(written, tc);
|
|
if (tc.isNull()) break;
|
|
stc = tc;
|
|
stc.movePosition(QTextCursor::Left);
|
|
stc.select(QTextCursor::WordUnderCursor);
|
|
if (!stc.selectedText().isEmpty() && stc.selectedText().trimmed() != written)
|
|
acwl << stc.selectedText();
|
|
}
|
|
acwl.removeDuplicates();
|
|
ACPair acl;
|
|
acl.first = -1;
|
|
foreach (const QString & s, acwl)
|
|
acl.second << StringsPair("", s);
|
|
ret << acl;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
void QCodeEdit::invokeAutoCompletition(bool force) {
|
|
QTextCursor tc = textCode->textCursor(), stc = tc;
|
|
if (tc.isNull()) {
|
|
completer.hide();
|
|
return;
|
|
}
|
|
int line = tc.block().firstLineNumber();
|
|
if (completer.isVisible()) {
|
|
if (auto_comp_pl != line) {
|
|
completer.hide();
|
|
auto_comp_pl = line;
|
|
return;
|
|
}
|
|
}
|
|
QString doc = textCode->toPlainText();
|
|
auto_comp_pl = line;
|
|
completer.clear();
|
|
int spos = tc.position(), cpos = spos;
|
|
tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor);
|
|
QStringList scope;
|
|
QString written = tc.selectedText().trimmed();
|
|
//qDebug() << "\n*** invokeAutoCompletition ***";
|
|
if (written != "_" && !written.leftJustified(1)[0].isLetterOrNumber()) {
|
|
written.clear();
|
|
} else {
|
|
cpos = skipCWord(doc, spos);
|
|
if (cpos >= 0)
|
|
written = doc.mid(cpos, spos - cpos).trimmed();
|
|
}
|
|
while (cpos >= 0) {
|
|
cpos--;
|
|
//qDebug() << "char =" << doc.mid(cpos, 1);
|
|
if (doc.mid(cpos, 1) != ".") break;
|
|
QChar c = doc.mid(cpos - 1, 1).leftJustified(1)[0];
|
|
int ppos = cpos;
|
|
if (c == '\"' || c == ')' || c == ']') {
|
|
cpos = skipRange(doc, cpos, pairChar(c), c, '\\');
|
|
//qDebug() << "range" << cpos;
|
|
if (cpos < 0) break;
|
|
}
|
|
int npos = skipCWord(doc, cpos);
|
|
if (npos < 0) break;
|
|
scope.push_front(doc.mid(npos, ppos - npos));
|
|
cpos = npos;
|
|
}
|
|
ACList acl(autoCompletitionList(scope, written));
|
|
//qDebug() << written << scope << acl.size();
|
|
if (scope.isEmpty() && written.isEmpty() && !force) {
|
|
completer.hide();
|
|
return;
|
|
}
|
|
acl << wordsCompletitionList(written);
|
|
QFont bf(font());
|
|
bf.setBold(true);
|
|
foreach (const ACPair & ac, acl) {
|
|
if (ac.second.isEmpty()) continue;
|
|
ACClass acc = ac_classes.value(ac.first);
|
|
QTreeWidgetItem * gi = new QTreeWidgetItem();
|
|
gi->setText(0, acc.name);
|
|
gi->setTextAlignment(0, Qt::AlignCenter);
|
|
gi->setTextAlignment(1, Qt::AlignCenter);
|
|
gi->setFont(0, bf);
|
|
gi->setBackgroundColor(0, Qt::lightGray);
|
|
gi->setFlags(Qt::ItemIsEnabled);
|
|
completer.addTopLevelItem(gi);
|
|
gi->setFirstColumnSpanned(true);
|
|
foreach (const StringsPair & s, ac.second) {
|
|
QTreeWidgetItem * ni = new QTreeWidgetItem();
|
|
ni->setIcon(0, acc.icon);
|
|
ni->setText(0, s.first);
|
|
ni->setText(1, s.second);
|
|
completer.addTopLevelItem(ni);
|
|
}
|
|
}
|
|
if (completer.topLevelItemCount() > 1)
|
|
completer.setCurrentItem(completer.topLevelItem(1));
|
|
if (completer.isHidden())
|
|
completer.move(textCode->mapToGlobal(textCode->cursorRect().bottomRight()));
|
|
completer.setVisible(completer.topLevelItemCount() > 0);
|
|
}
|
|
|
|
|
|
void QCodeEdit::commitCompletition() {
|
|
if (completer.currentItem() == 0) return;
|
|
if (!completer.currentItem()->flags().testFlag(Qt::ItemIsSelectable)) return;
|
|
QString ins = completer.currentItem()->text(1), ret = completer.currentItem()->text(0);
|
|
QTextCursor tc = textCode->textCursor(), stc = tc;
|
|
tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
|
|
bool ins_br = true, shifted = false;
|
|
if (!tc.selectedText().isEmpty()) {
|
|
if (!tc.selectedText()[0].isLetterOrNumber() && !tc.selectedText()[0].isSpace()) {
|
|
stc.movePosition(QTextCursor::Left);
|
|
shifted = true;
|
|
} else {
|
|
tc.movePosition(QTextCursor::Left);
|
|
tc.movePosition(QTextCursor::EndOfWord);
|
|
tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
|
|
}
|
|
if (!tc.selectedText().isEmpty())
|
|
if (tc.selectedText()[0].toLatin1() == '(')
|
|
ins_br = false;
|
|
}
|
|
if (ins.contains("("))
|
|
ins = ins.left(ins.indexOf("(")) + "()";
|
|
if (!ins_br && ins.endsWith("()"))
|
|
ins.chop(2);
|
|
tc = stc;
|
|
tc.select(QTextCursor::WordUnderCursor);
|
|
if (!tc.selectedText().leftJustified(1)[0].isLetterOrNumber()) {
|
|
tc = stc;
|
|
if (shifted)
|
|
tc.movePosition(QTextCursor::Right);
|
|
}
|
|
textCode->setTextCursor(tc);
|
|
textCode->textCursor().insertText(ins);
|
|
tc = textCode->textCursor();
|
|
if (ins_br) {
|
|
if (ret == "void") {
|
|
tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
|
|
if (tc.selectedText() != ";") {
|
|
textCode->textCursor().insertText(";");
|
|
tc.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2);
|
|
}
|
|
}
|
|
if (ins.endsWith(")") && !completer.currentItem()->text(1).endsWith("()")) {
|
|
tc.movePosition(QTextCursor::Left);
|
|
textCode->setTextCursor(tc);
|
|
}
|
|
} else {
|
|
if (completer.currentItem()->text(1).endsWith(")")) {
|
|
tc.movePosition(QTextCursor::Right);
|
|
textCode->setTextCursor(tc);
|
|
}
|
|
if (completer.currentItem()->text(1).endsWith("()")) {
|
|
tc.movePosition(QTextCursor::Right);
|
|
textCode->setTextCursor(tc);
|
|
}
|
|
}
|
|
completer.hide();
|
|
}
|
|
|
|
|
|
void QCodeEdit::textEdit_cursorPositionChanged() {
|
|
es_line.cursor = textCode->textCursor();
|
|
es_line.cursor.select(QTextCursor::LineUnderCursor);
|
|
es_line.cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
|
|
applyExtraSelection();
|
|
}
|
|
|
|
|
|
void QCodeEdit::textEdit_textChanged() {
|
|
updateLines();
|
|
}
|
|
|
|
|
|
void QCodeEdit::textEdit_selectionChanged() {
|
|
es_selected.clear();
|
|
QString sf = textCode->textCursor().selectedText();
|
|
if (sf.trimmed().isEmpty() || sf.contains("\n")) {
|
|
applyExtraSelection();
|
|
return;
|
|
}
|
|
QTextCursor tc(textCode->document()->begin());
|
|
QTextEdit::ExtraSelection es;
|
|
es.format.setBackground(QColor(251, 250, 150));
|
|
while (true) {
|
|
tc = textCode->document()->find(sf, tc, QTextDocument::FindCaseSensitively | QTextDocument::FindWholeWords);
|
|
if (tc.isNull()) break;
|
|
es.cursor = tc;
|
|
es_selected << es;
|
|
}
|
|
applyExtraSelection();
|
|
}
|
|
|
|
|
|
void QCodeEdit::setShowSpaces(bool yes) {
|
|
spaces_ = yes;
|
|
QTextOption to = textCode->document()->defaultTextOption();
|
|
to.setFlags(yes ? QTextOption::ShowTabsAndSpaces : (QTextOption::Flags)0);
|
|
textCode->document()->setDefaultTextOption(to);
|
|
}
|