1483 lines
43 KiB
C++
1483 lines
43 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>
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
|
# include <QWindow>
|
|
#endif
|
|
#include "ecombobox.h"
|
|
#include "qad_types.h"
|
|
#include "ui_qcodeedit.h"
|
|
|
|
Q_DECLARE_METATYPE(QTextCursor)
|
|
|
|
QCodeEdit::QCodeEdit(QWidget * parent): QWidget(parent) {
|
|
qRegisterMetaType<QTextCursor>();
|
|
ui = new Ui::QCodeEdit();
|
|
ui->setupUi(this);
|
|
ui->widgetSearch->hide();
|
|
ui->comboSearch->installEventFilter(this);
|
|
ui->comboReplace->installEventFilter(this);
|
|
prev_lc = auto_comp_pl = cur_search_ind = pos_press = pos_el_press = -1;
|
|
timer = 0;
|
|
_ignore_focus_out = _destructor = _replacing = false;
|
|
_first = true;
|
|
es_line.format.setBackground(QColor(240, 245, 240));
|
|
es_line.format.setProperty(QTextFormat::FullWidthSelection, true);
|
|
es_cursor.format.setBackground(QColor(220, 255, 200));
|
|
es_bracket.format.setBackground(QColor(180, 238, 180));
|
|
es_bracket.format.setForeground(Qt::red);
|
|
es_search.format.setBackground(QColor(255, 240, 10));
|
|
es_range.format.setBackground(QColor(230, 246, 255));
|
|
es_range.format.setProperty(QTextFormat::FullWidthSelection, true);
|
|
widget_help = new QFrame();
|
|
widget_help->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
|
|
widget_help->setFocusPolicy(Qt::NoFocus);
|
|
widget_help->setFrameShadow(QFrame::Sunken);
|
|
widget_help->setFrameShape(QFrame::StyledPanel);
|
|
widget_help->setLayout(new QBoxLayout(QBoxLayout::TopToBottom));
|
|
widget_help->layout()->setContentsMargins(0, 0, 0, 0);
|
|
for (int i = 0; i < 2; ++i) {
|
|
lbl_help[i] = new IconedLabel();
|
|
lbl_help[i]->setFrameShadow(QFrame::Plain);
|
|
lbl_help[i]->setFrameShape(QFrame::NoFrame);
|
|
lbl_help[i]->setDirection(IconedLabel::RightToLeft);
|
|
widget_help->layout()->addWidget(lbl_help[i]);
|
|
}
|
|
lbl_help[1]->setIcon(QIcon(":/icons/f1.png"));
|
|
lbl_help[1]->setText(tr("Press F1 for details"));
|
|
completer = new QTreeWidget();
|
|
completer->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
|
|
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);
|
|
|
|
ui->textCode->setCursorWidth(qMax<int>(qRound(fontHeight() / 10.), 1));
|
|
ui->textLines->viewport()->setAutoFillBackground(false);
|
|
ui->textLines->viewport()->setCursor(Qt::ArrowCursor);
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
|
|
ui->textLines->setFixedWidth(ui->textLines->fontMetrics().horizontalAdvance(" "));
|
|
#else
|
|
ui->textLines->setFixedWidth(ui->textLines->fontMetrics().width(" "));
|
|
#endif
|
|
|
|
QAction * a = new QAction(this); ui->textCode->addAction(a);
|
|
a->setShortcut(QKeySequence("Shift+Tab"));
|
|
a->setShortcutContext(Qt::WidgetShortcut);
|
|
connect(a, SIGNAL(triggered()), this, SLOT(deindent()));
|
|
a = new QAction(this); ui->textCode->addAction(a);
|
|
a->setShortcut(QKeySequence("Ctrl+D"));
|
|
a->setShortcutContext(Qt::WidgetShortcut);
|
|
connect(a, SIGNAL(triggered()), this, SLOT(deleteLine()));
|
|
a = new QAction(this); ui->textCode->addAction(a);
|
|
a->setShortcut(QKeySequence("Ctrl+Return"));
|
|
a->setShortcutContext(Qt::WidgetShortcut);
|
|
connect(a, SIGNAL(triggered()), this, SLOT(newLine()));
|
|
a = new QAction(this); ui->textCode->addAction(a);
|
|
a->setShortcut(QKeySequence("Ctrl+Up"));
|
|
a->setShortcutContext(Qt::WidgetShortcut);
|
|
connect(a, SIGNAL(triggered()), this, SLOT(scrollUp()));
|
|
a = new QAction(this); ui->textCode->addAction(a);
|
|
a->setShortcut(QKeySequence("Ctrl+Down"));
|
|
a->setShortcutContext(Qt::WidgetShortcut);
|
|
connect(a, SIGNAL(triggered()), this, SLOT(scrollDown()));
|
|
a = new QAction(this); ui->textCode->addAction(a);
|
|
a->setShortcut(QKeySequence("Ctrl+Shift+Return"));
|
|
a->setShortcutContext(Qt::WidgetShortcut);
|
|
connect(a, SIGNAL(triggered()), this, SLOT(newLineBefore()));
|
|
ui->frame->setFocusProxy(ui->textCode);
|
|
QTextOption to = ui->textLines->document()->defaultTextOption();
|
|
to.setAlignment(Qt::AlignTop | Qt::AlignRight);
|
|
ui->textLines->document()->setDefaultTextOption(to);
|
|
/*to = ui->textCode->document()->defaultTextOption();
|
|
to.setFlags(QTextOption::SuppressColors);
|
|
ui->textCode->document()->setDefaultTextOption(to);*/
|
|
setShowSpaces(true);
|
|
|
|
a = new QAction(this);
|
|
a->setShortcut(QKeySequence("Ctrl+F"));
|
|
a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
|
connect(a, SIGNAL(triggered(bool)), this, SLOT(search_triggered()));
|
|
addAction(a);
|
|
|
|
/*a = new QAction(this);
|
|
a->setShortcut(QKeySequence("Esc"));
|
|
a->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
|
connect(a, SIGNAL(triggered(bool)), this, SLOT(hideSearch()));
|
|
addAction(a);*/
|
|
|
|
connect(completer, SIGNAL(itemDoubleClicked(QTreeWidgetItem * ,int)), this, SLOT(commitCompletition()));
|
|
connect(ui->textCode->verticalScrollBar(), SIGNAL(valueChanged(int)), ui->textLines->verticalScrollBar(), SLOT(setValue(int)));
|
|
connect(ui->textCode->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(hideHelp()));
|
|
connect(ui->textCode, SIGNAL(textChanged()), this, SLOT(textEdit_textChanged()));
|
|
connect(ui->textCode, SIGNAL(textChanged()), this, SIGNAL(textChanged()));
|
|
connect(ui->textCode, SIGNAL(cursorPositionChanged()), this, SLOT(textEdit_cursorPositionChanged()));
|
|
connect(ui->textCode, SIGNAL(selectionChanged()), this, SLOT(textEdit_selectionChanged()));
|
|
connect(ui->comboSearch->lineEdit(), SIGNAL(returnPressed()), this, SLOT(searchNext()));
|
|
connect(ui->comboReplace->lineEdit(), SIGNAL(returnPressed()), this, SLOT(on_buttonReplaceSearch_clicked()));
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
|
connect(qApp, &QGuiApplication::focusWindowChanged, this, [this](QWindow*w){if (w == 0) {hideHelp(); completer->hide();}});
|
|
#endif
|
|
updateLines();
|
|
|
|
registerAutoCompletitionClass(-1, QCodeEdit::Keyword, "Words", QIcon(":/icons/code-word.png"));
|
|
}
|
|
|
|
|
|
QCodeEdit::~QCodeEdit() {
|
|
_destructor = true;
|
|
delete completer;
|
|
//for (int i = 0; i < 2; ++i)
|
|
// delete lbl_help[i];
|
|
delete widget_help;
|
|
//delete ui;
|
|
}
|
|
|
|
|
|
QTextCursor QCodeEdit::textCursor() const {
|
|
return ui->textCode->textCursor();
|
|
}
|
|
|
|
|
|
QTextDocument * QCodeEdit::document() const {
|
|
return ui->textCode->document();
|
|
}
|
|
|
|
|
|
void QCodeEdit::setDocument(QTextDocument * doc) {
|
|
if (document()) {
|
|
document()->setProperty("_cursor", QVariant::fromValue(textCursor()));
|
|
document()->setProperty("_vpos", textEdit()->verticalScrollBar()->value());
|
|
}
|
|
ui->textCode->setEnabled(doc);
|
|
ui->textLines->setEnabled(doc);
|
|
documentUnset();
|
|
if (!doc) {
|
|
ui->textCode->setDocument(0);
|
|
documentChanged(0);
|
|
return;
|
|
}
|
|
if (!qobject_cast<QPlainTextDocumentLayout*>(doc->documentLayout()))
|
|
doc->setDocumentLayout(new QPlainTextDocumentLayout(doc));
|
|
ui->textCode->setDocument(doc);
|
|
ui->textCode->setCursorWidth(qMax<int>(qRound(fontHeight() / 10.), 1));
|
|
setShowSpaces(spaces_);
|
|
if (doc->property("_cursor").isValid()) {
|
|
setTextCursor(doc->property("_cursor").value<QTextCursor>());
|
|
textEdit()->verticalScrollBar()->setValue(doc->property("_vpos").toInt());
|
|
}
|
|
documentChanged(doc);
|
|
doc->setDefaultFont(editorFont());
|
|
updateLines();
|
|
}
|
|
|
|
|
|
void QCodeEdit::setTextCursor(const QTextCursor & c) {
|
|
ui->textCode->setTextCursor(c);
|
|
}
|
|
|
|
|
|
void QCodeEdit::centerCursor() {
|
|
ui->textCode->centerCursor();
|
|
updateLines();
|
|
}
|
|
|
|
|
|
void QCodeEdit::insertText(const QString & text) {
|
|
ui->textCode->insertPlainText(text);
|
|
updateLines();
|
|
}
|
|
|
|
|
|
void QCodeEdit::appendText(const QString & text) {
|
|
ui->textCode->appendPlainText(text);
|
|
updateLines();
|
|
}
|
|
|
|
|
|
void QCodeEdit::setCustomExtraSelection(const QList<QTextEdit::ExtraSelection> & es) {
|
|
es_custom = es;
|
|
applyExtraSelection();
|
|
}
|
|
|
|
|
|
QRect QCodeEdit::cursorRect() const {
|
|
return ui->textCode->cursorRect();
|
|
}
|
|
|
|
|
|
QRect QCodeEdit::cursorRect(const QTextCursor & cursor) const {
|
|
return ui->textCode->cursorRect(cursor);
|
|
}
|
|
|
|
|
|
QString QCodeEdit::text() const {
|
|
return ui->textCode->toPlainText();
|
|
}
|
|
|
|
|
|
QStringList QCodeEdit::cursorScope() const {
|
|
return cursor_scope;
|
|
}
|
|
|
|
|
|
bool QCodeEdit::showLineNumbers() const {
|
|
return ui->textLines->isVisible();
|
|
}
|
|
|
|
|
|
void QCodeEdit::setEditorFont(QFont f) {
|
|
ui->textCode->setFont(f);
|
|
ui->textLines->setFont(f);
|
|
}
|
|
|
|
|
|
QFont QCodeEdit::editorFont() const {
|
|
return ui->textCode->font();
|
|
}
|
|
|
|
|
|
QPlainTextEdit * QCodeEdit::textEdit() const {
|
|
return ui->textCode;
|
|
}
|
|
|
|
|
|
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 (_destructor) return QWidget::eventFilter(o, e);
|
|
if (e->type() == QEvent::Destroy) {
|
|
completer->removeEventFilter(this);
|
|
ui->textCode->removeEventFilter(this);
|
|
ui->textCode->viewport()->removeEventFilter(this);
|
|
ui->textLines->viewport()->removeEventFilter(this);
|
|
return QWidget::eventFilter(o, e);
|
|
}
|
|
if (ui->textLines) {
|
|
if (o == ui->textLines->viewport()) {/*
|
|
if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonRelease ||
|
|
e->type() == QEvent::MouseMove || e->type() == QEvent::MouseButtonDblClick) {
|
|
#if (QT_VERSION < 0x050000)
|
|
const_cast<QPoint&>(((QMouseEvent*)e)->pos()) = QPoint(0, ((QMouseEvent*)e)->pos().y());
|
|
#else
|
|
const_cast<QPointF&>(((QMouseEvent*)e)->localPos()) = QPointF(0, ((QMouseEvent*)e)->localPos().y());
|
|
#endif
|
|
QApplication::sendEvent(ui->textCode->viewport(), e);
|
|
return true;
|
|
}*/
|
|
QTextCursor tc;
|
|
int tcpos = 0;
|
|
switch (e->type()) {
|
|
case QEvent::MouseButtonPress:
|
|
if (!isEnabled()) break;
|
|
tc = ui->textCode->cursorForPosition(((QMouseEvent*)e)->pos());
|
|
tc.movePosition(QTextCursor::EndOfLine);
|
|
pos_el_press = tc.anchor();
|
|
tc.movePosition(QTextCursor::StartOfLine);
|
|
pos_press = tc.anchor();
|
|
if (!tc.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor))
|
|
tc.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
|
|
ui->textCode->setFocus();
|
|
ui->textCode->setTextCursor(tc);
|
|
return true;
|
|
case QEvent::MouseMove:
|
|
if (!isEnabled()) break;
|
|
tc = ui->textCode->cursorForPosition(((QMouseEvent*)e)->pos());
|
|
tc.movePosition(QTextCursor::StartOfLine);
|
|
if (pos_press == tc.anchor()) {
|
|
if (!tc.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor))
|
|
tc.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
|
|
ui->textCode->setTextCursor(tc);
|
|
return true;
|
|
}
|
|
if (pos_press < tc.anchor()) {
|
|
if (!tc.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor))
|
|
tc.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
|
|
}
|
|
tcpos = tc.position();
|
|
tc.setPosition(pos_press < tc.anchor() ? pos_press : pos_el_press);
|
|
tc.setPosition(tcpos, QTextCursor::KeepAnchor);
|
|
ui->textCode->setTextCursor(tc);
|
|
return true;
|
|
case QEvent::Wheel:
|
|
if (!isEnabled()) break;
|
|
QApplication::sendEvent(ui->textCode->viewport(), e);
|
|
return true;
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
if (o == completer) {
|
|
//qDebug() << o << e;
|
|
if (e->type() == QEvent::WindowActivate)
|
|
_ignore_focus_out = true;
|
|
//qDebug() << e;
|
|
return QWidget::eventFilter(o, e);
|
|
}
|
|
if (o == ui->comboSearch || o == ui->comboReplace) {
|
|
//qDebug() << o << e;
|
|
if (e->type() == QEvent::KeyPress) {
|
|
if (((QKeyEvent * )e)->key() == Qt::Key_Escape) {
|
|
hideHelp();
|
|
if (completer->isVisible())
|
|
completer->hide();
|
|
else
|
|
hideSearch();
|
|
}
|
|
}
|
|
//qDebug() << e;
|
|
return QWidget::eventFilter(o, e);
|
|
}
|
|
if (ui->textCode) {
|
|
if (o == ui->textCode->viewport()) {
|
|
if (e->type() == QEvent::MouseButtonPress) {
|
|
completer->hide();
|
|
hideHelp();
|
|
}
|
|
return QWidget::eventFilter(o, e);
|
|
}
|
|
if (o == ui->textCode) {
|
|
//qDebug() << e;
|
|
QMetaObject::invokeMethod(this, "syncScrolls", Qt::QueuedConnection);
|
|
QKeyEvent * ke;
|
|
QChar kc(0);
|
|
switch (e->type()) {
|
|
case QEvent::ToolTip: {
|
|
QTextCursor tc = ui->textCode->cursorForPosition(((QHelpEvent*)e)->pos());
|
|
tc.select(QTextCursor::WordUnderCursor);
|
|
raiseHelp(tc);
|
|
}
|
|
break;
|
|
case QEvent::KeyPress:
|
|
ke = (QKeyEvent * )e;
|
|
//qDebug() << "key" << ke;
|
|
switch (ke->key()) {
|
|
case Qt::Key_Space:
|
|
if (ke->modifiers().testFlag(Qt::ControlModifier)) {
|
|
invokeAutoCompletition(true);
|
|
return true;
|
|
}
|
|
break;
|
|
case Qt::Key_Escape:
|
|
hideHelp();
|
|
if (completer->isVisible())
|
|
completer->hide();
|
|
else
|
|
hideSearch();
|
|
break;
|
|
case Qt::Key_Up:
|
|
if (completer->isVisible()) {
|
|
previousCompletition();
|
|
return true;
|
|
}
|
|
completer->hide();
|
|
hideHelp();
|
|
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();
|
|
hideHelp();
|
|
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 (ui->textCode->textCursor().selectedText().isEmpty())
|
|
QMetaObject::invokeMethod(this, "autoIndent", Qt::QueuedConnection);
|
|
break;
|
|
case Qt::Key_Tab:
|
|
if (!ui->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();
|
|
hideHelp();
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
return QWidget::eventFilter(o, e);
|
|
}
|
|
|
|
|
|
void QCodeEdit::showEvent(QShowEvent * ) {
|
|
if (!_first) return;
|
|
_first = false;
|
|
completer->installEventFilter(this);
|
|
ui->textCode->installEventFilter(this);
|
|
ui->textCode->viewport()->installEventFilter(this);
|
|
ui->textLines->viewport()->installEventFilter(this);
|
|
}
|
|
|
|
|
|
void QCodeEdit::timerEvent(QTimerEvent * ) {
|
|
parse();
|
|
emit parseRequest();
|
|
killTimer(timer);
|
|
timer = 0;
|
|
}
|
|
|
|
|
|
void QCodeEdit::leaveEvent(QEvent * e) {
|
|
hideHelp();
|
|
QWidget::leaveEvent(e);
|
|
}
|
|
|
|
|
|
void QCodeEdit::changeEvent(QEvent * e) {
|
|
QWidget::changeEvent(e);
|
|
switch (e->type()) {
|
|
case QEvent::LanguageChange:
|
|
ui->retranslateUi(this);
|
|
lbl_help[1]->setText(tr("Press F1 for details"));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
char antiBracket(char c) {
|
|
switch (c) {
|
|
case '(': return ')';
|
|
case '[': return ']';
|
|
case '{': return '}';
|
|
case '<': return '>';
|
|
case ')': return '(';
|
|
case ']': return '[';
|
|
case '}': return '{';
|
|
case '>': return '<';
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void QCodeEdit::highlightBrackets() {
|
|
es_brackets.clear();
|
|
QTextCursor stc = ui->textCode->textCursor(), tc;
|
|
QTextEdit::ExtraSelection es;
|
|
stc.setPosition(stc.position());
|
|
QTextCursor::MoveOperation mop[2] = {QTextCursor::Left, QTextCursor::Right};
|
|
QString mbr[2] = {")]}>", "([{<"};
|
|
for (int d = 0; d < 2; ++d) {
|
|
tc = stc;
|
|
tc.movePosition(mop[d], QTextCursor::KeepAnchor);
|
|
if (!tc.selectedText().isEmpty()) {
|
|
char ch = tc.selectedText()[0].toLatin1();
|
|
if (mbr[d].contains(ch)) {
|
|
es = es_bracket;
|
|
es.cursor = tc;
|
|
es_brackets << es;
|
|
QTextCursor ftc = tc;
|
|
int bcnt = 1; char fch = antiBracket(ch);
|
|
while (bcnt > 0) {
|
|
ftc.setPosition(ftc.position());
|
|
if (!ftc.movePosition(mop[d], QTextCursor::KeepAnchor)) break;
|
|
//qDebug() << tc.selectedText();
|
|
if (ftc.selectedText().isEmpty()) break;
|
|
if (ftc.selectedText()[0].toLatin1() == ch) ++bcnt;
|
|
if (ftc.selectedText()[0].toLatin1() == fch) --bcnt;
|
|
}
|
|
if (bcnt == 0) {
|
|
es.cursor = ftc;
|
|
es_brackets << es;
|
|
es.format = es_range.format;
|
|
es.cursor.setPosition(tc.position(), QTextCursor::KeepAnchor);
|
|
if (!es.cursor.selection().isEmpty())
|
|
es_brackets << es;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void QCodeEdit::applyExtraSelection() {
|
|
ui->textCode->setExtraSelections(QList<QTextEdit::ExtraSelection>() << es_line << es_selected
|
|
<< es_custom << es_brackets << es_search_list << es_cursor);
|
|
}
|
|
|
|
|
|
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::clearSearch() {
|
|
es_search_list.clear();
|
|
applyExtraSelection();
|
|
}
|
|
|
|
|
|
void QCodeEdit::moveToSearch() {
|
|
if (es_search_list.isEmpty()) return;
|
|
if (cur_search_ind < 0) cur_search_ind += es_search_list.size();
|
|
if (cur_search_ind >= es_search_list.size()) cur_search_ind = 0;
|
|
if (cur_search_ind < 0 || cur_search_ind >= es_search_list.size()) return;
|
|
ui->textCode->setTextCursor(es_search_list[cur_search_ind].cursor);
|
|
}
|
|
|
|
|
|
int QCodeEdit::searchIndFromCursor() {
|
|
if (es_search_list.isEmpty()) return -1;
|
|
int ci = ui->textCode->textCursor().anchor();
|
|
for (int i = 0; i < es_search_list.size(); ++i)
|
|
if (es_search_list[i].cursor.anchor() > ci)
|
|
return i - 1;
|
|
return -1;
|
|
}
|
|
|
|
|
|
void QCodeEdit::searchAll() {
|
|
QString st = ui->comboSearch->currentText();
|
|
es_search_list.clear();
|
|
if (!st.isEmpty() && !ui->widgetSearch->isHidden()) {
|
|
QTextDocument::FindFlags ff = QTextDocument::FindFlags();
|
|
if (ui->buttonSearchCase->isChecked()) ff |= QTextDocument::FindCaseSensitively;
|
|
if (ui->buttonSearchWord->isChecked()) ff |= QTextDocument::FindWholeWords;
|
|
QTextCursor tc(ui->textCode->document()->begin());
|
|
QTextEdit::ExtraSelection es = es_search;
|
|
while (true) {
|
|
tc = ui->textCode->document()->find(st, tc, ff);
|
|
if (tc.isNull()) break;
|
|
es.cursor = tc;
|
|
es_search_list << es;
|
|
}
|
|
}
|
|
applyExtraSelection();
|
|
QString ss;
|
|
if (es_search_list.isEmpty())
|
|
ss = "color: rgb(180, 0, 0);";
|
|
ui->comboSearch->lineEdit()->setStyleSheet(ss);
|
|
}
|
|
|
|
|
|
void QCodeEdit::search_triggered() {
|
|
QTextCursor tc = ui->textCode->textCursor();
|
|
QString st = tc.selectedText();
|
|
if (st.isEmpty()) {
|
|
tc.select(QTextCursor::WordUnderCursor);
|
|
st = tc.selectedText();
|
|
}
|
|
search(st);
|
|
//QMetaObject::invokeMethod(ui->comboSearch->lineEdit(), "returnPressed");
|
|
if (ui->comboSearch->findText(st) < 0)
|
|
ui->comboSearch->insertItem(0, st);
|
|
}
|
|
|
|
|
|
void QCodeEdit::syncScrolls() {
|
|
ui->textLines->verticalScrollBar()->setValue(ui->textCode->verticalScrollBar()->value());
|
|
ui->textLines->setHorizontalScrollBarPolicy(ui->textCode->horizontalScrollBar()->isVisible() ? Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff);
|
|
//qDebug() << "!!!";
|
|
}
|
|
|
|
|
|
void QCodeEdit::scrollUp() {
|
|
ui->textCode->verticalScrollBar()->setValue(ui->textCode->verticalScrollBar()->value() - 1);
|
|
}
|
|
|
|
|
|
void QCodeEdit::scrollDown() {
|
|
ui->textCode->verticalScrollBar()->setValue(ui->textCode->verticalScrollBar()->value() + 1);
|
|
}
|
|
|
|
|
|
void QCodeEdit::deleteLine() {
|
|
QTextCursor tc = ui->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);
|
|
ui->textCode->setTextCursor(tc);
|
|
}
|
|
|
|
|
|
void QCodeEdit::copyLineUp() {
|
|
QTextCursor tc = ui->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();
|
|
ui->textCode->setTextCursor(tc);
|
|
}
|
|
|
|
|
|
void QCodeEdit::copyLineDown() {
|
|
QTextCursor tc = ui->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();
|
|
ui->textCode->setTextCursor(tc);
|
|
}
|
|
|
|
|
|
void QCodeEdit::moveLineUp() {
|
|
QTextCursor tc = ui->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();
|
|
ui->textCode->setTextCursor(tc);
|
|
}
|
|
|
|
|
|
void QCodeEdit::moveLineDown() {
|
|
QTextCursor tc = ui->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();
|
|
ui->textCode->setTextCursor(tc);
|
|
}
|
|
|
|
|
|
void QCodeEdit::indent() {
|
|
QTextCursor tc = ui->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);
|
|
ui->textCode->setTextCursor(tc);
|
|
}
|
|
|
|
|
|
void QCodeEdit::deindent() {
|
|
QTextCursor tc = ui->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);
|
|
ui->textCode->setTextCursor(tc);
|
|
}
|
|
|
|
|
|
void QCodeEdit::autoIndent() {
|
|
QTextCursor tc = ui->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);
|
|
ui->textCode->setTextCursor(stc);
|
|
}
|
|
|
|
|
|
void QCodeEdit::scrollToTop() {
|
|
prev_lc = -1;
|
|
updateLines();
|
|
ui->textCode->verticalScrollBar()->setValue(0);
|
|
ui->textLines->verticalScrollBar()->setValue(0);
|
|
}
|
|
|
|
|
|
void QCodeEdit::newLine() {
|
|
QTextCursor tc = ui->textCode->textCursor();
|
|
tc.movePosition(QTextCursor::EndOfLine);
|
|
tc.insertText("\n");
|
|
ui->textCode->setTextCursor(tc);
|
|
QMetaObject::invokeMethod(this, "autoIndent", Qt::QueuedConnection);
|
|
}
|
|
|
|
|
|
void QCodeEdit::newLineBefore() {
|
|
QTextCursor tc = ui->textCode->textCursor();
|
|
tc.movePosition(QTextCursor::StartOfLine);
|
|
tc.insertText("\n");
|
|
tc.movePosition(QTextCursor::Up);
|
|
ui->textCode->setTextCursor(tc);
|
|
QMetaObject::invokeMethod(this, "autoIndent", Qt::QueuedConnection);
|
|
}
|
|
|
|
|
|
void QCodeEdit::setFocus() {
|
|
ui->textCode->setFocus();
|
|
}
|
|
|
|
|
|
void QCodeEdit::setText(const QString & t) {
|
|
ui->textCode->setPlainText(t);
|
|
}
|
|
|
|
|
|
void QCodeEdit::updateLines() {
|
|
if (timer > 0) killTimer(timer);
|
|
timer = startTimer(500);
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
|
# if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
|
|
ui->textCode->setTabStopDistance(ui->textCode->fontMetrics().horizontalAdvance(" "));
|
|
# else
|
|
ui->textCode->setTabStopDistance(ui->textCode->fontMetrics().width(" "));
|
|
# endif
|
|
#else
|
|
ui->textCode->setTabStopWidth(ui->textCode->fontMetrics().width(" "));
|
|
#endif
|
|
int lc = ui->textCode->document()->lineCount();
|
|
if (prev_lc == lc) return;
|
|
prev_lc = lc;
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
|
|
ui->textLines->setFixedWidth(ui->textLines->fontMetrics().horizontalAdvance(QString(" %1").arg(lc)));
|
|
#else
|
|
ui->textLines->setFixedWidth(ui->textLines->fontMetrics().width(QString(" %1").arg(lc)));
|
|
#endif
|
|
ui->textLines->clear();
|
|
for (int i = 1; i <= lc; ++i)
|
|
ui->textLines->appendPlainText(QString("%1").arg(i));
|
|
ui->textLines->verticalScrollBar()->setValue(ui->textCode->verticalScrollBar()->value());
|
|
}
|
|
|
|
|
|
QString QCodeEdit::selectArg(QString s, int arg) {
|
|
if (!s.contains('(') || arg < 0) return s;
|
|
QString ss = s.left(s.indexOf('('));
|
|
s.remove(0, ss.size());
|
|
if (s.startsWith('(')) s.remove(0, 1);
|
|
if (s.endsWith(')')) s.chop(1);
|
|
QStringList al = s.split(",");
|
|
QString ret = ss + "(";
|
|
for (int i = 0; i < al.size(); ++i) {
|
|
if (i > 0) ret += ", ";
|
|
if (i == arg) ret += "<span style=\"font-weight:600;\">";
|
|
ret += al[i].trimmed();
|
|
if (i == arg) ret += "</span>";
|
|
}
|
|
ret += ")";
|
|
return ret;
|
|
}
|
|
|
|
|
|
void QCodeEdit::raiseHelp(QTextCursor tc, int arg) {
|
|
bool ok;
|
|
QPair<QStringList, QString> scope = getScope(tc, &ok);
|
|
QString st = tc.selectedText();
|
|
if (arg >= 0) st = scope.second;
|
|
if (!ok || st.isEmpty()) {
|
|
hideHelp();
|
|
return;
|
|
}
|
|
ok = false;
|
|
ACList acl(autoCompletitionList(scope.first, scope.second));
|
|
foreach (const ACPair & i, acl) {
|
|
foreach (const StringsPair & s, i.second) {
|
|
QString ts = s.second;
|
|
//qDebug() << ts << st;
|
|
if (ts != st) {
|
|
if (ts.startsWith(st)) {
|
|
ts.remove(0, st.size());
|
|
ts = ts.trimmed();
|
|
if (!ts.isEmpty()) {
|
|
if (ts[0] != '(')
|
|
continue;
|
|
}
|
|
} else
|
|
continue;
|
|
}
|
|
//qDebug() << s.second << st;
|
|
ACClass acc = ac_classes.value(i.first);
|
|
lbl_help[0]->setIcon(acc.icon);
|
|
lbl_help[0]->setText(QString("<html><body>[%1] %2 %3</html></body>").arg(acc.name, s.first, selectArg(s.second, arg)));
|
|
ok = true;
|
|
break;
|
|
}
|
|
if (ok) break;
|
|
}
|
|
if (!ok) {
|
|
hideHelp();
|
|
return;
|
|
}
|
|
//qDebug() << "help found" << tc.selectionStart() << tc.selectionEnd();
|
|
es_cursor.cursor = tc;
|
|
applyExtraSelection();
|
|
//tc.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
|
|
lbl_help[0]->setFont(font());
|
|
qApp->processEvents();
|
|
widget_help->resize(widget_help->sizeHint());
|
|
qApp->processEvents();
|
|
QRect whr = ui->textCode->cursorRect(tc);
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
|
|
whr.setWidth(ui->textCode->fontMetrics().horizontalAdvance(st));
|
|
#else
|
|
whr.setWidth(ui->textCode->fontMetrics().width(st));
|
|
#endif
|
|
QPoint whp;
|
|
whp.setX(whr.left() - whr.width() - (widget_help->width() - whr.width()) / 2);
|
|
whp.setY(whr.top() - widget_help->height() - (fontHeight() / 3));
|
|
//qDebug() << whr << whp << widget_help->width() << ", " << st;
|
|
widget_help->move(ui->textCode->viewport()->mapToGlobal(whp));
|
|
widget_help->show();
|
|
widget_help->raise();
|
|
cursor_scope = scope.first;
|
|
cursor_scope << scope.second;
|
|
//qDebug() << "tooltip" << st;
|
|
}
|
|
|
|
|
|
void QCodeEdit::hideHelp() {
|
|
widget_help->hide();
|
|
es_cursor.cursor = QTextCursor();
|
|
cursor_scope.clear();
|
|
applyExtraSelection();
|
|
}
|
|
|
|
|
|
QTextCursor QCodeEdit::functionStart(QTextCursor tc, int * arg) {
|
|
QString doc = ui->textCode->toPlainText();
|
|
int bcnt = 0, a = 0, i = -1;
|
|
for (i = tc.position() - 1; i >= 0; --i) {
|
|
if (doc[i] == ')') bcnt++;
|
|
if (doc[i] == '(') {
|
|
if (bcnt == 0)
|
|
break;
|
|
else
|
|
bcnt--;
|
|
}
|
|
//if (doc[i] == '(') bcnt--;
|
|
if (doc[i] == ',' && bcnt == 0) a++;
|
|
}
|
|
if (i < 0) return QTextCursor();
|
|
if (arg) *arg = a;
|
|
QTextCursor ret(ui->textCode->document());
|
|
ret.setPosition(i);
|
|
//qDebug() << "found" << i << a;
|
|
return ret;
|
|
}
|
|
|
|
|
|
QCodeEdit::ACList QCodeEdit::wordsCompletitionList(const QString & written) const {
|
|
QCodeEdit::ACList ret;
|
|
if (!written.isEmpty()) {
|
|
QTextCursor tc = QTextCursor(ui->textCode->document()->begin()), stc;
|
|
QStringList acwl;
|
|
tc = QTextCursor(ui->textCode->document()->begin());
|
|
while (true) {
|
|
tc = ui->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;
|
|
}
|
|
|
|
|
|
QPair<QStringList, QString> QCodeEdit::getScope(QTextCursor tc, bool * ok) {
|
|
QPair<QStringList, QString> ret;
|
|
QTextCursor stc = tc;
|
|
if (tc.isNull()) {
|
|
completer->hide();
|
|
if (ok) *ok = false;
|
|
return ret;
|
|
}
|
|
int line = tc.block().firstLineNumber();
|
|
if (completer->isVisible()) {
|
|
if (auto_comp_pl != line) {
|
|
completer->hide();
|
|
auto_comp_pl = line;
|
|
if (ok) *ok = false;
|
|
return ret;
|
|
}
|
|
}
|
|
QString doc = ui->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;
|
|
}
|
|
ret.first = scope;
|
|
ret.second = written;
|
|
if (ok) *ok = true;
|
|
return ret;
|
|
}
|
|
|
|
|
|
void QCodeEdit::invokeAutoCompletition(bool force) {
|
|
int arg = -1;
|
|
QTextCursor htc = functionStart(ui->textCode->textCursor(), &arg);
|
|
if (!htc.isNull()) {
|
|
//qDebug() << "raise";
|
|
raiseHelp(htc, arg);
|
|
}
|
|
bool ok;
|
|
QPair<QStringList, QString> scope = getScope(ui->textCode->textCursor(), &ok);
|
|
if (!ok) return;
|
|
ACList acl(autoCompletitionList(scope.first, scope.second));
|
|
//qDebug() << written << scope << acl.size();
|
|
if (scope.first.isEmpty() && scope.second.isEmpty() && !force) {
|
|
completer->hide();
|
|
hideHelp();
|
|
return;
|
|
}
|
|
if (word_complet) acl << wordsCompletitionList(scope.second);
|
|
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->setBackground(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(ui->textCode->mapToGlobal(ui->textCode->cursorRect().bottomRight()));
|
|
if (completer->topLevelItemCount() > 0) {
|
|
completer->setVisible(true);
|
|
int sz = completer->verticalScrollBar()->isVisible() ? completer->verticalScrollBar()->width() : 0;
|
|
for (int i = 0; i < completer->header()->count(); ++i)
|
|
sz += ((QAbstractItemView*)completer)->sizeHintForColumn(i);
|
|
completer->resize(sz, fontHeight() * 16);
|
|
} else
|
|
completer->hide();
|
|
}
|
|
|
|
|
|
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 = ui->textCode->textCursor(), stc = tc;
|
|
tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
|
|
bool ins_br = true, shifted = false;
|
|
if (!tc.selectedText().isEmpty()) {
|
|
// if (tc.selectedText()[0].isSpace()) {
|
|
if (!tc.selectedText()[0].isLetterOrNumber() && !tc.selectedText()[0].isSpace() && !(tc.selectedText()[0] == '_')) {
|
|
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.selectedText().leftJustified(1)[0] == '_')) {
|
|
tc = stc;
|
|
if (shifted)
|
|
tc.movePosition(QTextCursor::Right);
|
|
}
|
|
ui->textCode->setTextCursor(tc);
|
|
ui->textCode->textCursor().insertText(ins);
|
|
tc = ui->textCode->textCursor();
|
|
if (ins_br) {
|
|
if (ret == "void") {
|
|
tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
|
|
if (tc.selectedText() != ";") {
|
|
ui->textCode->textCursor().insertText(";");
|
|
tc.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2);
|
|
}
|
|
}
|
|
if (ins.endsWith(")") && !completer->currentItem()->text(1).endsWith("()")) {
|
|
tc.movePosition(QTextCursor::Left);
|
|
ui->textCode->setTextCursor(tc);
|
|
}
|
|
} else {
|
|
if (completer->currentItem()->text(1).endsWith(")")) {
|
|
tc.movePosition(QTextCursor::Right);
|
|
ui->textCode->setTextCursor(tc);
|
|
}
|
|
if (completer->currentItem()->text(1).endsWith("()")) {
|
|
tc.movePosition(QTextCursor::Right);
|
|
ui->textCode->setTextCursor(tc);
|
|
}
|
|
}
|
|
completer->hide();
|
|
}
|
|
|
|
|
|
void QCodeEdit::textEdit_cursorPositionChanged() {
|
|
es_line.cursor = ui->textCode->textCursor();
|
|
es_line.cursor.select(QTextCursor::LineUnderCursor);
|
|
es_line.cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
|
|
highlightBrackets();
|
|
applyExtraSelection();
|
|
}
|
|
|
|
|
|
void QCodeEdit::textEdit_textChanged() {
|
|
if (_replacing) return;
|
|
searchAll();
|
|
updateLines();
|
|
}
|
|
|
|
|
|
void QCodeEdit::textEdit_selectionChanged() {
|
|
es_selected.clear();
|
|
QString sf = ui->textCode->textCursor().selectedText();
|
|
if (sf.trimmed().isEmpty() || sf.contains("\n")) {
|
|
applyExtraSelection();
|
|
return;
|
|
}
|
|
QTextCursor tc(ui->textCode->document()->begin());
|
|
QTextEdit::ExtraSelection es;
|
|
es.format.setBackground(QColor(251, 250, 150));
|
|
while (true) {
|
|
tc = ui->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 = ui->textCode->document()->defaultTextOption();
|
|
QTextOption::Flags tof = to.flags();
|
|
if (yes) tof |= QTextOption::ShowTabsAndSpaces;
|
|
else tof &= ~QTextOption::ShowTabsAndSpaces;
|
|
to.setFlags(tof);
|
|
ui->textCode->document()->setDefaultTextOption(to);
|
|
}
|
|
|
|
|
|
void QCodeEdit::setShowLineNumbers(bool yes) {
|
|
ui->textLines->setVisible(yes);
|
|
}
|
|
|
|
|
|
void QCodeEdit::search(const QString & t) {
|
|
ui->widgetSearch->show();
|
|
ui->comboSearch->setEditText(QString());
|
|
ui->comboSearch->setEditText(t);
|
|
ui->comboSearch->setFocus();
|
|
//searchAll();
|
|
searchNext(false);
|
|
}
|
|
|
|
|
|
void QCodeEdit::searchNext(bool next) {
|
|
if (es_search_list.isEmpty())
|
|
return;
|
|
cur_search_ind = searchIndFromCursor() + (next ? 1 : 0);
|
|
moveToSearch();
|
|
}
|
|
|
|
|
|
void QCodeEdit::searchPrevious() {
|
|
if (es_search_list.isEmpty())
|
|
return;
|
|
cur_search_ind = searchIndFromCursor() - 1;
|
|
moveToSearch();
|
|
}
|
|
|
|
|
|
void QCodeEdit::hideSearch() {
|
|
ui->widgetSearch->hide();
|
|
searchAll();
|
|
}
|
|
|
|
|
|
void QCodeEdit::on_comboSearch_currentTextChanged(const QString & t) {
|
|
searchAll();
|
|
searchNext(false);
|
|
}
|
|
|
|
|
|
void QCodeEdit::on_buttonReplace_clicked() {
|
|
if (es_search_list.isEmpty() || cur_search_ind < 0 || cur_search_ind >= es_search_list.size()) return;
|
|
if (ui->textCode->textCursor() != es_search_list[cur_search_ind].cursor) return;
|
|
if (ui->textCode->textCursor().selectedText().size() != es_search_list[cur_search_ind].cursor.selectedText().size()) return;
|
|
ui->textCode->textCursor().insertText(ui->comboReplace->currentText());
|
|
}
|
|
|
|
|
|
void QCodeEdit::on_buttonReplaceSearch_clicked() {
|
|
on_buttonReplace_clicked();
|
|
searchNext();
|
|
}
|
|
|
|
|
|
void QCodeEdit::on_buttonReplaceAll_clicked() {
|
|
_replacing = true;
|
|
QString rt = ui->comboReplace->currentText();
|
|
for (int i = es_search_list.size() - 1; i >= 0; --i)
|
|
es_search_list[i].cursor.insertText(rt);
|
|
_replacing = false;
|
|
textEdit_textChanged();
|
|
}
|