diff --git a/qad_widgets/CMakeLists.txt b/qad_widgets/CMakeLists.txt index 7c3e7ed..7f60d28 100644 --- a/qad_widgets/CMakeLists.txt +++ b/qad_widgets/CMakeLists.txt @@ -4,7 +4,7 @@ find_package(Qt4 REQUIRED) include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${QT_INCLUDES}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wall") -#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3") set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS}") set(MOCS "qpiconsole.h" "spinslider.h" diff --git a/qad_widgets/qcodeedit.cpp b/qad_widgets/qcodeedit.cpp index 6949bab..4eb4ebd 100644 --- a/qad_widgets/qcodeedit.cpp +++ b/qad_widgets/qcodeedit.cpp @@ -17,26 +17,34 @@ QCodeEdit::QCodeEdit(QWidget * parent): QWidget(parent) { textCode = textLines = 0; timer = 0; - _ignore_focus_out = false; + _ignore_focus_out = _destructor = 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()-> + es_cursor.format.setBackground(QColor(220, 255, 200)); + lbl_help = new IconedLabel(); + lbl_help->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint); + lbl_help->setFocusPolicy(Qt::NoFocus); + lbl_help->setFrameShadow(QFrame::Sunken); + lbl_help->setFrameShape(QFrame::StyledPanel); + lbl_help->setDirection(IconedLabel::RightToLeft); + completer = new QTreeWidget(); + 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); + completer->header()->setStretchLastSection(true); + //completer->setColumnWidth(0, 180); + completer->resize(500, 200); textCode = new QPlainTextEdit(); textLines = new QPlainTextEdit(); textCode->setFrameShadow(QFrame::Plain); @@ -82,7 +90,7 @@ QCodeEdit::QCodeEdit(QWidget * parent): QWidget(parent) { textLines->document()->setDefaultTextOption(to); setShowSpaces(true); - connect(&completer, SIGNAL(itemDoubleClicked(QTreeWidgetItem * ,int)), this, SLOT(commitCompletition())); + 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())); @@ -95,8 +103,11 @@ QCodeEdit::QCodeEdit(QWidget * parent): QWidget(parent) { QCodeEdit::~QCodeEdit() { + _destructor = true; delete textCode; delete textLines; + delete completer; + delete lbl_help; } @@ -165,8 +176,9 @@ QChar QCodeEdit::pairChar(QChar c) { bool QCodeEdit::eventFilter(QObject * o, QEvent * e) { + if (_destructor) return QWidget::eventFilter(o, e); if (e->type() == QEvent::Destroy) { - completer.removeEventFilter(this); + completer->removeEventFilter(this); textCode->removeEventFilter(this); textCode->viewport()->removeEventFilter(this); textLines->viewport()->removeEventFilter(this); @@ -188,7 +200,7 @@ bool QCodeEdit::eventFilter(QObject * o, QEvent * e) { } } } - if (o == &completer) { + if (o == completer) { //qDebug() << o << e; if (e->type() == QEvent::WindowActivate) _ignore_focus_out = true; @@ -197,12 +209,20 @@ bool QCodeEdit::eventFilter(QObject * o, QEvent * e) { } if (textCode) { if (o == textCode->viewport()) { - if (e->type() == QEvent::MouseButtonPress) - completer.hide(); + if (e->type() == QEvent::MouseButtonPress) { + completer->hide(); + hideHelp(); + } + if (e->type() == QEvent::ToolTip) { + QTextCursor tc = textCode->cursorForPosition(((QHelpEvent*)e)->pos()); + tc.select(QTextCursor::WordUnderCursor); + raiseHelp(tc); + } return QWidget::eventFilter(o, e); } if (o == textCode) { //qDebug() << o << e; + QMetaObject::invokeMethod(this, "syncScrolls", Qt::QueuedConnection); QKeyEvent * ke; QChar kc(0); switch (e->type()) { @@ -216,14 +236,16 @@ bool QCodeEdit::eventFilter(QObject * o, QEvent * e) { } break; case Qt::Key_Escape: - completer.hide(); + completer->hide(); + hideHelp(); break; case Qt::Key_Up: - if (completer.isVisible()) { + if (completer->isVisible()) { previousCompletition(); return true; } - completer.hide(); + completer->hide(); + hideHelp(); if (ke->modifiers().testFlag(Qt::AltModifier)) { copyLineUp(); return true; @@ -234,11 +256,12 @@ bool QCodeEdit::eventFilter(QObject * o, QEvent * e) { } break; case Qt::Key_Down: - if (completer.isVisible()) { + if (completer->isVisible()) { nextCompletition(); return true; } - completer.hide(); + completer->hide(); + hideHelp(); if (ke->modifiers().testFlag(Qt::AltModifier)) { copyLineDown(); return true; @@ -252,8 +275,8 @@ bool QCodeEdit::eventFilter(QObject * o, QEvent * e) { 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())); + if (completer->isVisible()) { + qApp->sendEvent(completer, new QKeyEvent(e->type(), ke->key(), ke->modifiers())); return true; } break; @@ -261,13 +284,13 @@ bool QCodeEdit::eventFilter(QObject * o, QEvent * e) { case Qt::Key_Right: case Qt::Key_Backspace: case Qt::Key_Delete: - if (completer.isVisible()) + if (completer->isVisible()) QMetaObject::invokeMethod(this, "invokeAutoCompletition", Qt::QueuedConnection, Q_ARG(bool, false)); break; case Qt::Key_Return: - if (completer.isVisible()) { + if (completer->isVisible()) { commitCompletition(); - completer.hide(); + completer->hide(); return true; } if (textCode->textCursor().selectedText().isEmpty()) @@ -284,7 +307,7 @@ bool QCodeEdit::eventFilter(QObject * o, QEvent * e) { break; case Qt::Key_D: if (ke->modifiers().testFlag(Qt::ControlModifier)) { - completer.hide(); + completer->hide(); return true; } break; @@ -293,10 +316,10 @@ bool QCodeEdit::eventFilter(QObject * o, QEvent * e) { if (!ke->text().isEmpty()) kc = ke->text()[0]; if (kc == '.') { - completer.hide(); + completer->hide(); QMetaObject::invokeMethod(this, "invokeAutoCompletition", Qt::QueuedConnection, Q_ARG(bool, false)); } else { - if ((kc.isLetterOrNumber() || kc.toLatin1() == '_') && completer.isVisible()) + if ((kc.isLetterOrNumber() || kc.toLatin1() == '_') && completer->isVisible()) QMetaObject::invokeMethod(this, "invokeAutoCompletition", Qt::QueuedConnection, Q_ARG(bool, false)); } break; @@ -309,7 +332,8 @@ bool QCodeEdit::eventFilter(QObject * o, QEvent * e) { case QEvent::HideToParent: case QEvent::MouseButtonPress: //qDebug() << e; - completer.hide(); + completer->hide(); + hideHelp(); default: break; } } @@ -321,7 +345,7 @@ bool QCodeEdit::eventFilter(QObject * o, QEvent * e) { void QCodeEdit::showEvent(QShowEvent * ) { if (!_first) return; _first = false; - completer.installEventFilter(this); + completer->installEventFilter(this); textCode->installEventFilter(this); textCode->viewport()->installEventFilter(this); textLines->viewport()->installEventFilter(this); @@ -337,34 +361,39 @@ void QCodeEdit::timerEvent(QTimerEvent * ) { void QCodeEdit::applyExtraSelection() { - textCode->setExtraSelections(QList() << es_line << es_selected << es_custom); + textCode->setExtraSelections(QList() << es_line << es_selected << es_custom << 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)); + 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)); + if (ci >= completer->topLevelItemCount() - 2) return; + completer->setCurrentItem(completer->topLevelItem(ci + 2)); } } void QCodeEdit::previousCompletition() { - int ci = completer.currentIndex().row(); + int ci = completer->currentIndex().row(); if (ci <= 0) return; - if (completer.topLevelItem(ci - 1)->flags().testFlag(Qt::ItemIsSelectable)) - completer.setCurrentItem(completer.topLevelItem(ci - 1)); + 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)); + completer->setCurrentItem(completer->topLevelItem(ci - 2)); } } +void QCodeEdit::syncScrolls() { + textLines->verticalScrollBar()->setValue(textCode->verticalScrollBar()->value()); +} + + void QCodeEdit::deleteLine() { QTextCursor tc = textCode->textCursor(); tc.movePosition(QTextCursor::EndOfLine); @@ -606,6 +635,14 @@ void QCodeEdit::autoIndent() { } +void QCodeEdit::scrollToTop() { + prev_lc = -1; + updateLines(); + textCode->verticalScrollBar()->setValue(0); + textLines->verticalScrollBar()->setValue(0); +} + + void QCodeEdit::updateLines() { if (timer > 0) killTimer(timer); timer = startTimer(500); @@ -621,6 +658,106 @@ void QCodeEdit::updateLines() { } +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 += ""; + ret += al[i].trimmed(); + if (i == arg) ret += ""; + } + ret += ")"; + return ret; +} + + +void QCodeEdit::raiseHelp(QTextCursor tc, int arg) { + bool ok; + QPair scope = getScope(tc, &ok); + //qDebug() << "help" << scope; + 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->setIcon(acc.icon); + lbl_help->setText(QString("[%1] %2 %3").arg(acc.name, s.first, selectArg(s.second, arg))); + ok = true; + break; + } + if (ok) break; + } + if (!ok) { + hideHelp(); + return; + } + es_cursor.cursor = tc; + applyExtraSelection(); + tc.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, st.size()); + lbl_help->setFont(font()); + lbl_help->resize(lbl_help->sizeHint()); + lbl_help->move(textCode->mapToGlobal(textCode->cursorRect(tc).topLeft() - QPoint(0, lbl_help->height() + 8))); + lbl_help->show(); + //qDebug() << "tooltip" << st; +} + + +void QCodeEdit::hideHelp() { + lbl_help->hide(); + es_cursor.cursor = QTextCursor(); + applyExtraSelection(); +} + + +QTextCursor QCodeEdit::functionStart(QTextCursor tc, int * arg) { + QString doc = 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(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()) { @@ -647,23 +784,26 @@ QCodeEdit::ACList QCodeEdit::wordsCompletitionList(const QString & written) cons } -void QCodeEdit::invokeAutoCompletition(bool force) { - QTextCursor tc = textCode->textCursor(), stc = tc; +QPair QCodeEdit::getScope(QTextCursor tc, bool * ok) { + QPair ret; + QTextCursor stc = tc; if (tc.isNull()) { - completer.hide(); - return; + completer->hide(); + if (ok) *ok = false; + return ret; } int line = tc.block().firstLineNumber(); - if (completer.isVisible()) { + if (completer->isVisible()) { if (auto_comp_pl != line) { - completer.hide(); + completer->hide(); auto_comp_pl = line; - return; + if (ok) *ok = false; + return ret; } } QString doc = textCode->toPlainText(); auto_comp_pl = line; - completer.clear(); + completer->clear(); int spos = tc.position(), cpos = spos; tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor); QStringList scope; @@ -692,13 +832,31 @@ void QCodeEdit::invokeAutoCompletition(bool force) { scope.push_front(doc.mid(npos, ppos - npos)); cpos = npos; } - ACList acl(autoCompletitionList(scope, written)); + ret.first = scope; + ret.second = written; + if (ok) *ok = true; + return ret; +} + + +void QCodeEdit::invokeAutoCompletition(bool force) { + int arg = -1; + QTextCursor htc = functionStart(textCode->textCursor(), &arg); + if (!htc.isNull()) { + //qDebug() << "raise"; + raiseHelp(htc, arg); + } + bool ok; + QPair scope = getScope(textCode->textCursor(), &ok); + if (!ok) return; + ACList acl(autoCompletitionList(scope.first, scope.second)); //qDebug() << written << scope << acl.size(); - if (scope.isEmpty() && written.isEmpty() && !force) { - completer.hide(); + if (scope.first.isEmpty() && scope.second.isEmpty() && !force) { + completer->hide(); + hideHelp(); return; } - acl << wordsCompletitionList(written); + acl << wordsCompletitionList(scope.second); QFont bf(font()); bf.setBold(true); foreach (const ACPair & ac, acl) { @@ -711,28 +869,28 @@ void QCodeEdit::invokeAutoCompletition(bool force) { gi->setFont(0, bf); gi->setBackgroundColor(0, Qt::lightGray); gi->setFlags(Qt::ItemIsEnabled); - completer.addTopLevelItem(gi); + 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); + 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); + 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); + 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; @@ -771,21 +929,21 @@ void QCodeEdit::commitCompletition() { tc.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2); } } - if (ins.endsWith(")") && !completer.currentItem()->text(1).endsWith("()")) { + if (ins.endsWith(")") && !completer->currentItem()->text(1).endsWith("()")) { tc.movePosition(QTextCursor::Left); textCode->setTextCursor(tc); } } else { - if (completer.currentItem()->text(1).endsWith(")")) { + if (completer->currentItem()->text(1).endsWith(")")) { tc.movePosition(QTextCursor::Right); textCode->setTextCursor(tc); } - if (completer.currentItem()->text(1).endsWith("()")) { + if (completer->currentItem()->text(1).endsWith("()")) { tc.movePosition(QTextCursor::Right); textCode->setTextCursor(tc); } } - completer.hide(); + completer->hide(); } diff --git a/qad_widgets/qcodeedit.h b/qad_widgets/qcodeedit.h index fd3cff9..a2f0f17 100644 --- a/qad_widgets/qcodeedit.h +++ b/qad_widgets/qcodeedit.h @@ -4,6 +4,8 @@ #include #include #include +#include +#include "iconedlabel.h" QT_BEGIN_HEADER @@ -36,6 +38,7 @@ public: QRect cursorRect() const {return textCode->cursorRect();} QRect cursorRect(const QTextCursor & cursor) const {return textCode->cursorRect(cursor);} QString text() const {return textCode->toPlainText();} + QString helpWord() const {return es_cursor.cursor.selectedText();} bool showSpaces() const {return spaces_;} bool showLineNumbers() const {return textLines->isVisible();} @@ -50,7 +53,12 @@ protected: virtual ACList autoCompletitionList(const QStringList & scope, const QString & written) const {return ACList();} virtual void parse() {} + QString selectArg(QString s, int arg); + void raiseHelp(QTextCursor tc, int arg = -1); + void hideHelp(); + QTextCursor functionStart(QTextCursor tc, int * arg); ACList wordsCompletitionList(const QString & written) const; + QPair getScope(QTextCursor tc, bool * ok = 0); static int skipRange(const QString & s, int pos, QChar oc, QChar cc, QChar sc = QChar()); static int skipCWord(const QString & s, int pos); static bool matchWritten(QString s, QString w); @@ -66,12 +74,13 @@ private: }; QPlainTextEdit * textCode, * textLines; - QTreeWidget completer; - QTextEdit::ExtraSelection es_line; + QTreeWidget * completer; + IconedLabel * lbl_help; + QTextEdit::ExtraSelection es_line, es_cursor; QList es_selected, es_custom; QMap ac_classes; int prev_lc, auto_comp_pl, timer; - bool spaces_, _ignore_focus_out, _first; + bool spaces_, _ignore_focus_out, _first, _destructor; bool eventFilter(QObject * o, QEvent * e); void showEvent(QShowEvent * ); @@ -81,6 +90,7 @@ private: void previousCompletition(); private slots: + void syncScrolls(); void deleteLine(); void copyLineUp(); void copyLineDown(); @@ -89,7 +99,6 @@ private slots: void indent(); void deindent(); void autoIndent(); - void updateLines(); void invokeAutoCompletition(bool force = false); void commitCompletition(); void textEdit_cursorPositionChanged(); @@ -97,6 +106,8 @@ private slots: void textEdit_selectionChanged(); public slots: + void updateLines(); + void scrollToTop(); void setFocus() {textCode->setFocus();} void setText(const QString & t) {textCode->setPlainText(t);} void setShowSpaces(bool yes);