SHOW:
|
|
- or go back to the newest paste.
| 1 | #include "qpconsole.h" | |
| 2 | ||
| 3 | #include <QApplication> | |
| 4 | #include <QTextBlock> | |
| 5 | #include <QTextDocument> | |
| 6 | #include <QTextCursor> | |
| 7 | #include <QClipboard> | |
| 8 | - | #include "global.h" |
| 8 | + | |
| 9 | QPConsole::QPConsole(QWidget *parent) : | |
| 10 | QPlainTextEdit(parent), | |
| 11 | mHistoryPos(-1), | |
| 12 | mCommandLineReady(false) | |
| 13 | {
| |
| 14 | QPalette p = palette(); | |
| 15 | p.setColor(QPalette::Active, QPalette::Base, Qt::black); | |
| 16 | p.setColor(QPalette::Inactive, QPalette::Base, Qt::black); | |
| 17 | p.setColor(QPalette::Active, QPalette::Text, Qt::white); | |
| 18 | p.setColor(QPalette::Inactive, QPalette::Text, Qt::white); | |
| 19 | setPalette(p); | |
| 20 | ||
| 21 | QFont f; | |
| 22 | #ifdef Q_OS_LINUX | |
| 23 | f.setFamily("Monospace");
| |
| 24 | f.setPointSize(10); | |
| 25 | #else | |
| 26 | f.setFamily("Lucida Console");
| |
| 27 | f.setPointSize(10); | |
| 28 | #endif | |
| 29 | f.setFixedPitch(true); | |
| 30 | setFont(f); | |
| 31 | ||
| 32 | setCursorWidth(QFontMetrics(font()).width(QChar('x')));
| |
| 33 | setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); | |
| 34 | setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); | |
| 35 | setUndoRedoEnabled(false); | |
| 36 | setLineWrapMode(QPlainTextEdit::NoWrap); | |
| 37 | setWordWrapMode(QTextOption::NoWrap); | |
| 38 | setBackgroundVisible(false); | |
| 39 | setFrameStyle(QFrame::NoFrame); | |
| 40 | setTabStopWidth(40); | |
| 41 | setAcceptDrops(false); | |
| 42 | setPrefix("terminal> ");
| |
| 43 | setPrefixColor(QColor(140, 255, 50)); | |
| 44 | f.setBold(true); | |
| 45 | setPrefixFont(f); | |
| 46 | ||
| 47 | prepareCommandLine(); | |
| 48 | } | |
| 49 | ||
| 50 | void QPConsole::setPrefix(const QString &prefix) | |
| 51 | {
| |
| 52 | mPrefix = prefix; | |
| 53 | } | |
| 54 | ||
| 55 | void QPConsole::setPrefixColor(const QColor &color) | |
| 56 | {
| |
| 57 | mPrefixColor = color; | |
| 58 | } | |
| 59 | ||
| 60 | void QPConsole::setPrefixFont(const QFont &font) | |
| 61 | {
| |
| 62 | mPrefixFont = font; | |
| 63 | } | |
| 64 | ||
| 65 | bool QPConsole::inCommandLine() const | |
| 66 | {
| |
| 67 | return mCommandLineReady && document()->blockCount()-1 == textCursor().blockNumber() && textCursor().positionInBlock() >= mPrefix.length(); | |
| 68 | } | |
| 69 | ||
| 70 | void QPConsole::processCommand() | |
| 71 | {
| |
| 72 | QString inputString = document()->lastBlock().text(); | |
| 73 | if (inputString.startsWith(mPrefix)) | |
| 74 | inputString.remove(0, mPrefix.length()); | |
| 75 | if (!inputString.trimmed().isEmpty()) | |
| 76 | mHistory.append(inputString); | |
| 77 | mHistoryPos = -1; | |
| 78 | inputString = inputString.trimmed(); | |
| 79 | ||
| 80 | QStringList params; | |
| 81 | QString cmd; | |
| 82 | int p = inputString.indexOf(' ');
| |
| 83 | if (p > -1) | |
| 84 | {
| |
| 85 | cmd = inputString.mid(0, p); | |
| 86 | params = getParams(inputString.mid(p+1)); | |
| 87 | } else | |
| 88 | cmd = inputString; | |
| 89 | ||
| 90 | QTextCursor cur(document()->lastBlock()); | |
| 91 | cur.movePosition(QTextCursor::EndOfBlock); | |
| 92 | cur.insertBlock(); | |
| 93 | setTextCursor(cur); | |
| 94 | mCommandLineReady = false; | |
| 95 | emit command(cmd, params); | |
| 96 | } | |
| 97 | ||
| 98 | QStringList QPConsole::getParams(QString str) | |
| 99 | {
| |
| 100 | QStringList result; | |
| 101 | str = str.trimmed(); | |
| 102 | if (str.isEmpty()) | |
| 103 | return result; | |
| 104 | ||
| 105 | bool escaped = false; | |
| 106 | bool quoted = false; | |
| 107 | QString param; | |
| 108 | int i = 0; | |
| 109 | while (i < str.size()) | |
| 110 | {
| |
| 111 | if (escaped) | |
| 112 | {
| |
| 113 | escaped = false; | |
| 114 | param += str[i]; | |
| 115 | } else if (str[i] == '\\') | |
| 116 | {
| |
| 117 | escaped = true; | |
| 118 | } else if (str[i] == '"') | |
| 119 | {
| |
| 120 | quoted = !quoted; | |
| 121 | if (!param.isEmpty() && !(i+1>=str.size() || str[i+1]==' ')) // only add quote if not first and last, embracing a parameter | |
| 122 | param += str[i]; | |
| 123 | } else if (str[i] == ' ') | |
| 124 | {
| |
| 125 | if (quoted) | |
| 126 | {
| |
| 127 | param += str[i]; | |
| 128 | } else if (!param.isEmpty()) | |
| 129 | {
| |
| 130 | result.append(param); | |
| 131 | param.clear(); | |
| 132 | } | |
| 133 | } else | |
| 134 | {
| |
| 135 | param += str[i]; | |
| 136 | } | |
| 137 | ++i; | |
| 138 | } | |
| 139 | if (!param.isEmpty()) | |
| 140 | {
| |
| 141 | result.append(param); | |
| 142 | param.clear(); | |
| 143 | } | |
| 144 | return result; | |
| 145 | } | |
| 146 | ||
| 147 | void QPConsole::keyPressEvent(QKeyEvent *event) | |
| 148 | {
| |
| 149 | if (inCommandLine()) | |
| 150 | {
| |
| 151 | // clear selection that spans multiple blocks (or prefix characters) (would overwrite previous command lines): | |
| 152 | QTextCursor cur = textCursor(); | |
| 153 | if (cur.hasSelection()) | |
| 154 | {
| |
| 155 | if (document()->findBlock(cur.selectionStart()) != document()->findBlock(cur.selectionEnd()) || // spans multiple blocks (including command line) | |
| 156 | cur.selectionStart()-cur.block().position() < mPrefix.length() || // spans prefix | |
| 157 | cur.selectionEnd()-cur.block().position() < mPrefix.length() ) // spans prefix | |
| 158 | {
| |
| 159 | cur.clearSelection(); | |
| 160 | if (cur.positionInBlock() < mPrefix.length()) | |
| 161 | cur.setPosition(cur.block().position()+mPrefix.length()); | |
| 162 | setTextCursor(cur); | |
| 163 | } | |
| 164 | } | |
| 165 | if (cur.positionInBlock() == mPrefix.length()) | |
| 166 | {
| |
| 167 | cur.setCharFormat(QTextCharFormat()); // make sure we don't pick up format from prefix | |
| 168 | setTextCursor(cur); | |
| 169 | } | |
| 170 | // react to keystroke: | |
| 171 | if (event->matches(QKeySequence::MoveToPreviousLine)) // history up | |
| 172 | {
| |
| 173 | if (mHistory.isEmpty() || mHistoryPos >= mHistory.size()-1) | |
| 174 | return; | |
| 175 | ++mHistoryPos; | |
| 176 | int index = mHistory.size()-mHistoryPos-1; | |
| 177 | QTextCursor cur(document()->lastBlock()); | |
| 178 | cur.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, mPrefix.length()); | |
| 179 | cur.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); | |
| 180 | cur.removeSelectedText(); | |
| 181 | cur.setCharFormat(QTextCharFormat()); | |
| 182 | cur.insertText(mHistory.at(index)); | |
| 183 | setTextCursor(cur); | |
| 184 | } else if (event->matches(QKeySequence::MoveToNextLine)) // history down | |
| 185 | {
| |
| 186 | if (mHistory.isEmpty() || mHistoryPos <= 0) | |
| 187 | return; | |
| 188 | --mHistoryPos; | |
| 189 | int index = mHistory.size()-mHistoryPos-1; | |
| 190 | QTextCursor cur(document()->lastBlock()); | |
| 191 | cur.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, mPrefix.length()); | |
| 192 | cur.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); | |
| 193 | cur.removeSelectedText(); | |
| 194 | cur.setCharFormat(QTextCharFormat()); | |
| 195 | cur.insertText(mHistory.at(index)); | |
| 196 | setTextCursor(cur); | |
| 197 | } else if (event->matches(QKeySequence::Paste)) // paste text, do it manually to remove text char formatting and newlines | |
| 198 | {
| |
| 199 | QString pasteText = QApplication::clipboard()->text(); | |
| 200 | pasteText.replace("\n", "").replace("\r", "");
| |
| 201 | cur.setCharFormat(QTextCharFormat()); | |
| 202 | cur.insertText(pasteText); | |
| 203 | setTextCursor(cur); | |
| 204 | } else if (event->key() == Qt::Key_Return) // process command | |
| 205 | {
| |
| 206 | processCommand(); | |
| 207 | } else if (event->key() == Qt::Key_Backspace) // only allow backspace if we wouldn't delete last char of prefix | |
| 208 | {
| |
| 209 | if (cur.positionInBlock() > mPrefix.length()) | |
| 210 | QPlainTextEdit::keyPressEvent(event); | |
| 211 | } else if (!event->matches(QKeySequence::Close) && | |
| 212 | !event->matches(QKeySequence::New) && | |
| 213 | !event->matches(QKeySequence::Open) && | |
| 214 | !event->matches(QKeySequence::Preferences) && | |
| 215 | !event->matches(QKeySequence::Bold) && | |
| 216 | !event->matches(QKeySequence::Italic) && | |
| 217 | !event->matches(QKeySequence::InsertLineSeparator) && | |
| 218 | !event->matches(QKeySequence::InsertParagraphSeparator) && | |
| 219 | !event->matches(QKeySequence::Redo) && | |
| 220 | !event->matches(QKeySequence::Undo) && | |
| 221 | !event->matches(QKeySequence::DeleteStartOfWord)) | |
| 222 | {
| |
| 223 | QPlainTextEdit::keyPressEvent(event); | |
| 224 | } | |
| 225 | } else // cursor position not in command line | |
| 226 | {
| |
| 227 | if (event->matches(QKeySequence::MoveToEndOfDocument) || | |
| 228 | event->matches(QKeySequence::MoveToEndOfBlock) || | |
| 229 | event->matches(QKeySequence::MoveToEndOfLine) || | |
| 230 | event->matches(QKeySequence::MoveToStartOfDocument) || | |
| 231 | event->matches(QKeySequence::MoveToStartOfBlock) || | |
| 232 | event->matches(QKeySequence::MoveToStartOfLine) || | |
| 233 | event->matches(QKeySequence::MoveToNextLine) || | |
| 234 | event->matches(QKeySequence::MoveToNextWord) || | |
| 235 | event->matches(QKeySequence::MoveToNextChar) || | |
| 236 | event->matches(QKeySequence::MoveToPreviousLine) || | |
| 237 | event->matches(QKeySequence::MoveToPreviousWord) || | |
| 238 | event->matches(QKeySequence::MoveToPreviousChar) || | |
| 239 | event->matches(QKeySequence::SelectAll) || | |
| 240 | event->matches(QKeySequence::SelectEndOfDocument) || | |
| 241 | event->matches(QKeySequence::SelectEndOfBlock) || | |
| 242 | event->matches(QKeySequence::SelectEndOfLine) || | |
| 243 | event->matches(QKeySequence::SelectStartOfDocument) || | |
| 244 | event->matches(QKeySequence::SelectStartOfBlock) || | |
| 245 | event->matches(QKeySequence::SelectStartOfLine) || | |
| 246 | event->matches(QKeySequence::SelectNextLine) || | |
| 247 | event->matches(QKeySequence::SelectNextWord) || | |
| 248 | event->matches(QKeySequence::SelectNextChar) || | |
| 249 | event->matches(QKeySequence::SelectPreviousLine) || | |
| 250 | event->matches(QKeySequence::SelectPreviousWord) || | |
| 251 | event->matches(QKeySequence::SelectPreviousChar) || | |
| 252 | event->matches(QKeySequence::Copy) ) | |
| 253 | QPlainTextEdit::keyPressEvent(event); | |
| 254 | } | |
| 255 | } | |
| 256 | ||
| 257 | void QPConsole::print(QString str) | |
| 258 | {
| |
| 259 | QTextCursor cur(document()->lastBlock()); | |
| 260 | cur.setCharFormat(QTextCharFormat()); | |
| 261 | cur.insertText(str); | |
| 262 | cur.movePosition(QTextCursor::EndOfBlock); | |
| 263 | cur.insertBlock(); | |
| 264 | setTextCursor(cur); | |
| 265 | mCommandLineReady = false; | |
| 266 | } | |
| 267 | ||
| 268 | void QPConsole::prepareCommandLine() | |
| 269 | {
| |
| 270 | QTextCursor cur(document()->lastBlock()); | |
| 271 | if (!document()->lastBlock().text().isEmpty()) | |
| 272 | {
| |
| 273 | cur.movePosition(QTextCursor::EndOfBlock); | |
| 274 | cur.insertBlock(); | |
| 275 | } | |
| 276 | QTextCharFormat fmt; | |
| 277 | fmt.setForeground(QBrush(mPrefixColor)); | |
| 278 | fmt.setFont(mPrefixFont); | |
| 279 | cur.setCharFormat(fmt); | |
| 280 | cur.insertText(mPrefix); | |
| 281 | cur.setCharFormat(QTextCharFormat()); | |
| 282 | setTextCursor(cur); | |
| 283 | mCommandLineReady = true; | |
| 284 | } |