#include "QConsoleWidget.h" #include "QConsoleIODevice.h" #include #include #include #include #include #include #include #include #include #include #include #include #include void QConsoleWidget::setModelProcess(ModelProcess* app) { this->application = app; // 创建dockerWidget qDebug() << u8"initCommandDocker,等待初始化命令行窗口"; QDockWidget* dockWidget_command = this->application->getdockWidget_command(u8"命令行窗口"); dockWidget_command->setHidden(false); dockWidget_command->setWidget(this); this->qs = new ScriptSession(this); // 创建脚本 QTimer::singleShot(200,qs, SLOT(REPL())); // 绑定application //this->qs->settingApplication() this->qs->settingApplication(this->application); qDebug() << u8"initCommandDocker,结束初始化命令行窗口"; } QConsoleWidget::QConsoleWidget(QWidget* parent) : QPlainTextEdit(parent), mode_(Output), completer_(0) { iodevice_ = new QConsoleIODevice(this,this); QTextCharFormat fmt = currentCharFormat(); for(int i=0; itextCursor(); textCursor.movePosition(QTextCursor::End); textCursor.setPosition(inpos_,QTextCursor::KeepAnchor); QString code = textCursor.selectedText(); code.replace(QChar::ParagraphSeparator,QChar::LineFeed); return code; } void QConsoleWidget::handleReturnKey() { QString code = getCommandLine(); // start new block appendPlainText(QString()); setMode(Output); QTextCursor textCursor = this->textCursor(); textCursor.movePosition(QTextCursor::End); setTextCursor(textCursor); // Update the history if (!code.isEmpty()) history_.add(code); // append the newline char and // send signal / update iodevice code += "\n"; if (iodevice_->isOpen()) iodevice_->consoleWidgetInput(code); else { emit consoleCommand(code); } } void QConsoleWidget::handleTabKey() { QTextCursor tc = this->textCursor(); int anchor = tc.anchor(); int position = tc.position(); tc.setPosition(inpos_); tc.setPosition(position, QTextCursor::KeepAnchor); QString text = tc.selectedText().trimmed(); tc.setPosition(anchor, QTextCursor::MoveAnchor); tc.setPosition(position, QTextCursor::KeepAnchor); if (text.isEmpty()) { tc.insertText(" "); } else { updateCompleter(); if (completer_ && completer_->completionCount() == 1) { insertCompletion(completer_->currentCompletion()); completer_->popup()->hide(); } } } void QConsoleWidget::updateCompleter() { if (!completer_) return; // if the completer is first shown, mark // the text position QTextCursor textCursor = this->textCursor(); if (!completer_->popup()->isVisible()) { completion_pos_ = textCursor.position(); // qDebug() << "show completer, pos " << completion_pos_; } // Get the text between the current cursor position // and the start of the input textCursor.setPosition(inpos_, QTextCursor::KeepAnchor); QString commandText = textCursor.selectedText(); // qDebug() << "code to complete: " << commandText; // Call the completer to update the completion model // Place and show the completer if there are available completions int count; if ((count = completer_->updateCompletionModel(commandText))) { // qDebug() << "found " << count << " completions"; // Get a QRect for the cursor at the start of the // current word and then translate it down 8 pixels. textCursor = this->textCursor(); textCursor.movePosition(QTextCursor::StartOfWord); QRect cr = this->cursorRect(textCursor); cr.translate(0, 8); cr.setWidth(completer_->popup()->sizeHintForColumn(0) + completer_->popup()->verticalScrollBar()->sizeHint().width()); completer_->complete(cr); } else { // qDebug() << "no completions - hiding"; completer_->popup()->hide(); } } void QConsoleWidget::checkCompletionTriggers(const QString &txt) { if (!completer_ || completion_triggers_.isEmpty() || txt.isEmpty()) return; foreach(const QString& tr, completion_triggers_) { if (tr.endsWith(txt)) { QTextCursor tc = this->textCursor(); tc.movePosition(QTextCursor::Left,QTextCursor::KeepAnchor,tr.length()); if (tc.selectedText()==tr) { updateCompleter(); return; } } } } void QConsoleWidget::insertCompletion(const QString& completion) { QTextCursor tc = textCursor(); tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, tc.position() - inpos_ - completer_->insertPos()); tc.insertText(completion,chanFormat_[StandardInput]); setTextCursor(tc); } void QConsoleWidget::setCompleter(QConsoleWidgetCompleter *c) { if (completer_) { completer_->setWidget(0); QObject::disconnect(completer_, SIGNAL(activated(const QString&)), this, SLOT(insertCompletion(const QString&))); } completer_ = c; if (completer_) { completer_->setWidget(this); QObject::connect(completer_, SIGNAL(activated(const QString&)), this, SLOT(insertCompletion(const QString&))); } } void QConsoleWidget::keyPressEvent(QKeyEvent* e) { if (completer_ && completer_->popup()->isVisible()) { // The following keys are forwarded by the completer to the widget switch (e->key()) { case Qt::Key_Tab: case Qt::Key_Enter: case Qt::Key_Return: case Qt::Key_Escape: case Qt::Key_Backtab: e->ignore(); return; // let the completer do default behavior default: break; } } QTextCursor textCursor = this->textCursor(); bool selectionInEditZone = isSelectionInEditZone(); // check for user abort request if (e->modifiers() & Qt::ControlModifier) { if (e->key()==Qt::Key_Q) // Ctrl-Q aborts { emit abortEvaluation(); e->accept(); return; } } // Allow copying anywhere in the console ... if (e->key() == Qt::Key_C && e->modifiers() == Qt::ControlModifier) { if (textCursor.hasSelection()) copy(); e->accept(); return; } // the rest of key events are ignored during output mode if (mode()!=Input) { e->ignore(); return; } // Allow cut only if the selection is limited to the interactive area ... if (e->key() == Qt::Key_X && e->modifiers() == Qt::ControlModifier) { if (selectionInEditZone) cut(); e->accept(); return; } // Allow paste only if the selection is in the interactive area ... if (e->key() == Qt::Key_V && e->modifiers() == Qt::ControlModifier) { if (selectionInEditZone || isCursorInEditZone()) { const QMimeData* const clipboard = QApplication::clipboard()->mimeData(); const QString text = clipboard->text(); if (!text.isNull()) { textCursor.insertText(text,channelCharFormat(StandardInput)); } } e->accept(); return; } int key = e->key(); int shiftMod = e->modifiers() == Qt::ShiftModifier; if (history_.isActive() && key!=Qt::Key_Up && key!=Qt::Key_Down) history_.deactivate(); // Force the cursor back to the interactive area // for all keys except modifiers if (!isCursorInEditZone() && key != Qt::Key_Control && key != Qt::Key_Shift && key != Qt::Key_Alt) { textCursor.movePosition(QTextCursor::End); setTextCursor(textCursor); } switch (key) { case Qt::Key_Up: // Activate the history and move to the 1st matching history item if (!history_.isActive()) history_.activate(getCommandLine()); if (history_.move(true)) replaceCommandLine(history_.currentValue()); else QApplication::beep(); e->accept(); break; case Qt::Key_Down: if (history_.move(false)) replaceCommandLine(history_.currentValue()); else QApplication::beep(); e->accept(); case Qt::Key_Left: if (textCursor.position() > inpos_) QPlainTextEdit::keyPressEvent(e); else { QApplication::beep(); e->accept(); } break; case Qt::Key_Delete: e->accept(); if (selectionInEditZone) cut(); else { // cursor must be in edit zone if (textCursor.position() < inpos_) QApplication::beep(); else QPlainTextEdit::keyPressEvent(e); } break; case Qt::Key_Backspace: e->accept(); if (selectionInEditZone) cut(); else { // cursor must be in edit zone if (textCursor.position() <= inpos_) QApplication::beep(); else QPlainTextEdit::keyPressEvent(e); } break; case Qt::Key_Tab: e->accept(); handleTabKey(); return; case Qt::Key_Home: e->accept(); textCursor.setPosition(inpos_, shiftMod ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); setTextCursor(textCursor); break; case Qt::Key_Enter: case Qt::Key_Return: e->accept(); handleReturnKey(); break; case Qt::Key_Escape: e->accept(); replaceCommandLine(QString()); break; default: e->accept(); setCurrentCharFormat(chanFormat_[StandardInput]); QPlainTextEdit::keyPressEvent(e); // check if the last key triggers a completion checkCompletionTriggers(e->text()); break; } if (completer_ && completer_->popup()->isVisible()) { // if the completer is visible check if it should be updated if (this->textCursor().position()popup()->hide(); else updateCompleter(); } } void QConsoleWidget::contextMenuEvent(QContextMenuEvent *event) { QMenu *menu = createStandardContextMenu(); QAction* a; if ((a = menu->findChild("edit-cut"))) a->setEnabled(canCut()); if ((a = menu->findChild("edit-delete"))) a->setEnabled(canCut()); if ((a = menu->findChild("edit-paste"))) a->setEnabled(canPaste()); menu->exec(event->globalPos()); delete menu; } bool QConsoleWidget::isSelectionInEditZone() const { QTextCursor textCursor = this->textCursor(); if (!textCursor.hasSelection()) return false; int selectionStart = textCursor.selectionStart(); int selectionEnd = textCursor.selectionEnd(); return selectionStart >= inpos_ && selectionEnd >= inpos_; } bool QConsoleWidget::isCursorInEditZone() const { return textCursor().position()>=inpos_; } bool QConsoleWidget::canPaste() const { QTextCursor textCursor = this->textCursor(); if (textCursor.position()textCursor(); textCursor.movePosition(QTextCursor::End); textCursor.setPosition(inpos_, QTextCursor::KeepAnchor); // ... and replace it with new string. textCursor.insertText(str,chanFormat_[StandardInput]); // move to the end of the document textCursor.movePosition(QTextCursor::End); setTextCursor(textCursor); } void QConsoleWidget::write(const QString & message, const QTextCharFormat& fmt) { QTextCharFormat currfmt = currentCharFormat(); QTextCursor tc = textCursor(); if (mode()==Input) { // in Input mode output messages are inserted // before the edit block // get offset of current pos from the end int editpos = tc.position(); tc.movePosition(QTextCursor::End); editpos = tc.position() - editpos; // convert the input pos as relative from the end inpos_ = tc.position() - inpos_; // insert block tc.movePosition(QTextCursor::StartOfBlock); tc.insertBlock(); tc.movePosition(QTextCursor::PreviousBlock); tc.insertText(message,fmt); tc.movePosition(QTextCursor::End); // restore input pos inpos_ = tc.position() - inpos_; // restore the edit pos tc.movePosition(QTextCursor::Left,QTextCursor::MoveAnchor,editpos); setTextCursor(tc); setCurrentCharFormat(currfmt); } else { // in output mode messages are appended QTextCursor tc1 = tc; tc1.movePosition(QTextCursor::End); // check is cursor was not at the end // (e.g. had been moved per mouse action) bool needsRestore = tc1.position()!=tc.position(); // insert text setTextCursor(tc1); textCursor().insertText(message,fmt); ensureCursorVisible(); // restore cursor if needed if (needsRestore) setTextCursor(tc); } } void QConsoleWidget::writeStdOut(const QString& s) { write(s,chanFormat_[StandardOutput]); } void QConsoleWidget::writeStdErr(const QString& s) { write(s,chanFormat_[StandardError]); } /////////////////// QConsoleWidget::History ///////////////////// #define HISTORY_FILE ".command_history.lst" QConsoleWidget::History QConsoleWidget::history_; QConsoleWidget::History::History(void) : pos_(0), active_(false), maxsize_(10000) { QFile f(HISTORY_FILE); if (f.open(QFile::ReadOnly)) { QTextStream is(&f); while(!is.atEnd()) add(is.readLine()); } } QConsoleWidget::History::~History(void) { QFile f(HISTORY_FILE); if (f.open(QFile::WriteOnly | QFile::Truncate)) { QTextStream os(&f); int n = strings_.size(); while(n>0) os << strings_.at(--n) << endl; } } void QConsoleWidget::History::add(const QString& str) { active_ = false; //if (strings_.contains(str)) return; if (strings_.size() == maxsize_) strings_.pop_back(); strings_.push_front(str); } void QConsoleWidget::History::activate(const QString& tk) { active_ = true; token_ = tk; pos_ = -1; } bool QConsoleWidget::History::move(bool dir) { if (active_) { int next = indexOf ( dir, pos_ ); if (pos_!=next) { pos_=next; return true; } else return false; } else return false; } int QConsoleWidget::History::indexOf(bool dir, int from) const { int i = from, to = from; if (dir) { while(i < strings_.size()-1) { const QString& si = strings_.at(++i); if (si.startsWith(token_)) { return i; } } } else { while(i > 0) { const QString& si = strings_.at(--i); if (si.startsWith(token_)) { return i; } } return -1; } return to; } /////////////////// Stream manipulators ///////////////////// QTextStream &waitForInput(QTextStream &s) { QConsoleIODevice* d = qobject_cast(s.device()); if (d) d->waitForReadyRead(-1); return s; } QTextStream &inputMode(QTextStream &s) { QConsoleIODevice* d = qobject_cast(s.device()); if (d && d->widget()) d->widget()->setMode(QConsoleWidget::Input); return s; } QTextStream &outChannel(QTextStream &s) { QConsoleIODevice* d = qobject_cast(s.device()); if (d) d->setCurrentWriteChannel(QConsoleWidget::StandardOutput); return s; } QTextStream &errChannel(QTextStream &s) { QConsoleIODevice* d = qobject_cast(s.device()); if (d) d->setCurrentWriteChannel(QConsoleWidget::StandardError); return s; }