[71025] | 1 | /* $Id: UIGuestControlConsole.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
| 3 | * VBox Qt GUI - UIGuestControlConsole class implementation.
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[106061] | 7 | * Copyright (C) 2016-2024 Oracle and/or its affiliates.
|
---|
[71025] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
| 11 | *
|
---|
| 12 | * This program is free software; you can redistribute it and/or
|
---|
| 13 | * modify it under the terms of the GNU General Public License
|
---|
| 14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 15 | * License.
|
---|
| 16 | *
|
---|
| 17 | * This program is distributed in the hope that it will be useful, but
|
---|
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 20 | * General Public License for more details.
|
---|
| 21 | *
|
---|
| 22 | * You should have received a copy of the GNU General Public License
|
---|
| 23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 24 | *
|
---|
| 25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[71025] | 26 | */
|
---|
| 27 |
|
---|
| 28 | /* Qt includes: */
|
---|
[76606] | 29 | #include <QVBoxLayout>
|
---|
| 30 | #include <QApplication>
|
---|
| 31 | #include <QTextBlock>
|
---|
[71025] | 32 |
|
---|
| 33 | /* GUI includes: */
|
---|
[76606] | 34 | #include "UIGuestControlConsole.h"
|
---|
[77822] | 35 | #include "UIGuestControlInterface.h"
|
---|
[71025] | 36 |
|
---|
[77822] | 37 | UIGuestControlConsole::UIGuestControlConsole(const CGuest &comGuest, QWidget* parent /* = 0 */)
|
---|
[71025] | 38 | :QPlainTextEdit(parent)
|
---|
[77822] | 39 | , m_comGuest(comGuest)
|
---|
[71025] | 40 | , m_strGreet("Welcome to 'Guest Control Console'. Type 'help' for help\n")
|
---|
| 41 | , m_strPrompt("$>")
|
---|
| 42 | , m_uCommandHistoryIndex(0)
|
---|
[77822] | 43 | , m_pControlInterface(0)
|
---|
[71025] | 44 | {
|
---|
[77822] | 45 | m_pControlInterface = new UIGuestControlInterface(this, m_comGuest);
|
---|
| 46 |
|
---|
| 47 | connect(m_pControlInterface, &UIGuestControlInterface::sigOutputString,
|
---|
| 48 | this, &UIGuestControlConsole::sltOutputReceived);
|
---|
| 49 |
|
---|
[71025] | 50 | /* Configure this: */
|
---|
[71066] | 51 | setUndoRedoEnabled(false);
|
---|
[71025] | 52 | setWordWrapMode(QTextOption::NoWrap);
|
---|
| 53 | reset();
|
---|
[71145] | 54 |
|
---|
| 55 | m_tabDictinary.insert("username", 0);
|
---|
| 56 | m_tabDictinary.insert("createsession", 0);
|
---|
| 57 | m_tabDictinary.insert("exe", 0);
|
---|
| 58 | m_tabDictinary.insert("sessionid", 0);
|
---|
| 59 | m_tabDictinary.insert("sessionname", 0);
|
---|
| 60 | m_tabDictinary.insert("timeout", 0);
|
---|
| 61 | m_tabDictinary.insert("password", 0);
|
---|
| 62 | m_tabDictinary.insert("start", 0);
|
---|
| 63 | m_tabDictinary.insert("ls", 0);
|
---|
| 64 | m_tabDictinary.insert("stat", 0);
|
---|
[71025] | 65 | }
|
---|
| 66 |
|
---|
[77822] | 67 | void UIGuestControlConsole::commandEntered(const QString &strCommand)
|
---|
| 68 | {
|
---|
| 69 | if (m_pControlInterface)
|
---|
| 70 | m_pControlInterface->putCommand(strCommand);
|
---|
| 71 | }
|
---|
| 72 |
|
---|
| 73 | void UIGuestControlConsole::sltOutputReceived(const QString &strOutput)
|
---|
| 74 | {
|
---|
| 75 | putOutput(strOutput);
|
---|
| 76 | }
|
---|
| 77 |
|
---|
[71025] | 78 | void UIGuestControlConsole::reset()
|
---|
| 79 | {
|
---|
| 80 | clear();
|
---|
| 81 | startNextLine();
|
---|
| 82 | insertPlainText(m_strGreet);
|
---|
| 83 | startNextLine();
|
---|
| 84 | }
|
---|
| 85 |
|
---|
| 86 | void UIGuestControlConsole::startNextLine()
|
---|
| 87 | {
|
---|
| 88 | moveCursor(QTextCursor::End);
|
---|
| 89 | insertPlainText(m_strPrompt);
|
---|
| 90 | moveCursor(QTextCursor::End);
|
---|
| 91 | }
|
---|
| 92 |
|
---|
| 93 |
|
---|
| 94 | void UIGuestControlConsole::putOutput(const QString &strOutput)
|
---|
| 95 | {
|
---|
[71049] | 96 | if (strOutput.isNull() || strOutput.length() <= 0)
|
---|
[71025] | 97 | return;
|
---|
| 98 |
|
---|
| 99 | bool newLineNeeded = getCommandString().isEmpty();
|
---|
| 100 |
|
---|
| 101 | QString strOwn("\n");
|
---|
| 102 | strOwn.append(strOutput);
|
---|
| 103 | moveCursor(QTextCursor::End);
|
---|
| 104 | insertPlainText(strOwn);
|
---|
| 105 | moveCursor(QTextCursor::End);
|
---|
| 106 |
|
---|
[71049] | 107 | if (newLineNeeded)
|
---|
[71025] | 108 | {
|
---|
| 109 | insertPlainText("\n");
|
---|
| 110 | startNextLine();
|
---|
| 111 | }
|
---|
[71089] | 112 | }
|
---|
[71025] | 113 |
|
---|
| 114 | void UIGuestControlConsole::keyPressEvent(QKeyEvent *pEvent)
|
---|
| 115 | {
|
---|
[71052] | 116 | /* Check if we at the bottom most line.*/
|
---|
| 117 | bool lastLine = blockCount() == (textCursor().blockNumber() +1);
|
---|
[71025] | 118 |
|
---|
| 119 | switch (pEvent->key()) {
|
---|
| 120 | case Qt::Key_PageUp:
|
---|
| 121 | case Qt::Key_Up:
|
---|
| 122 | {
|
---|
| 123 | replaceLineContent(getPreviousCommandFromHistory(getCommandString()));
|
---|
| 124 | break;
|
---|
| 125 | }
|
---|
| 126 | case Qt::Key_PageDown:
|
---|
| 127 | case Qt::Key_Down:
|
---|
| 128 | {
|
---|
[71066] | 129 | replaceLineContent(getNextCommandFromHistory(getCommandString()));
|
---|
[71025] | 130 | break;
|
---|
| 131 | }
|
---|
| 132 | case Qt::Key_Backspace:
|
---|
| 133 | {
|
---|
| 134 | QTextCursor cursor = textCursor();
|
---|
[71052] | 135 | if (lastLine && cursor.positionInBlock() > m_strPrompt.length())
|
---|
[71025] | 136 | cursor.deletePreviousChar();
|
---|
| 137 | break;
|
---|
| 138 | }
|
---|
| 139 | case Qt::Key_Left:
|
---|
| 140 | case Qt::Key_Right:
|
---|
| 141 | {
|
---|
[71135] | 142 | if (textCursor().positionInBlock() > m_strPrompt.length()-1)
|
---|
[71025] | 143 | QPlainTextEdit::keyPressEvent(pEvent);
|
---|
| 144 | break;
|
---|
| 145 | }
|
---|
| 146 | case Qt::Key_Return:
|
---|
| 147 | case Qt::Key_Enter:
|
---|
| 148 | {
|
---|
[71052] | 149 | if (lastLine)
|
---|
[71025] | 150 | {
|
---|
[71052] | 151 | QString strCommand(getCommandString());
|
---|
| 152 | if (!strCommand.isEmpty())
|
---|
| 153 | {
|
---|
[77822] | 154 | commandEntered(strCommand);
|
---|
[71052] | 155 | if (!m_tCommandHistory.contains(strCommand))
|
---|
| 156 | m_tCommandHistory.push_back(strCommand);
|
---|
| 157 | m_uCommandHistoryIndex = m_tCommandHistory.size()-1;
|
---|
| 158 | moveCursor(QTextCursor::End);
|
---|
| 159 | QPlainTextEdit::keyPressEvent(pEvent);
|
---|
| 160 | startNextLine();
|
---|
| 161 | }
|
---|
[71025] | 162 | }
|
---|
| 163 | break;
|
---|
| 164 | }
|
---|
| 165 | case Qt::Key_Home:
|
---|
| 166 | {
|
---|
| 167 | QTextCursor cursor = textCursor();
|
---|
| 168 | cursor.movePosition(QTextCursor::StartOfLine);
|
---|
| 169 | cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, m_strPrompt.length());
|
---|
| 170 | setTextCursor(cursor);
|
---|
| 171 | break;
|
---|
| 172 | }
|
---|
[71145] | 173 | case Qt::Key_Tab:
|
---|
| 174 | completeByTab();
|
---|
| 175 | break;
|
---|
[71025] | 176 | default:
|
---|
[71066] | 177 | {
|
---|
[71089] | 178 | if (pEvent->modifiers() == Qt::ControlModifier && pEvent->key() == Qt::Key_C)
|
---|
[71066] | 179 | {
|
---|
[71052] | 180 | QPlainTextEdit::keyPressEvent(pEvent);
|
---|
[71066] | 181 | }
|
---|
| 182 | else
|
---|
| 183 | {
|
---|
| 184 | if (lastLine)
|
---|
| 185 | QPlainTextEdit::keyPressEvent(pEvent);
|
---|
| 186 | }
|
---|
| 187 | }
|
---|
[71025] | 188 | break;
|
---|
| 189 | }
|
---|
| 190 | }
|
---|
| 191 |
|
---|
| 192 | void UIGuestControlConsole::mousePressEvent(QMouseEvent *pEvent)
|
---|
| 193 | {
|
---|
[71049] | 194 | // Q_UNUSED(pEvent);
|
---|
| 195 | // setFocus();
|
---|
| 196 | QPlainTextEdit::mousePressEvent(pEvent);
|
---|
[71025] | 197 | }
|
---|
| 198 |
|
---|
| 199 | void UIGuestControlConsole::mouseDoubleClickEvent(QMouseEvent *pEvent)
|
---|
| 200 | {
|
---|
[71049] | 201 | //Q_UNUSED(pEvent);
|
---|
| 202 | QPlainTextEdit::mouseDoubleClickEvent(pEvent);
|
---|
[71025] | 203 | }
|
---|
| 204 |
|
---|
| 205 | void UIGuestControlConsole::contextMenuEvent(QContextMenuEvent *pEvent)
|
---|
| 206 | {
|
---|
[71052] | 207 | Q_UNUSED(pEvent);
|
---|
| 208 | //QPlainTextEdit::contextMenuEvent(pEvent);
|
---|
[71025] | 209 | }
|
---|
| 210 |
|
---|
| 211 | QString UIGuestControlConsole::getCommandString()
|
---|
| 212 | {
|
---|
| 213 | QTextDocument* pDocument = document();
|
---|
[71049] | 214 | if (!pDocument)
|
---|
[71025] | 215 | return QString();
|
---|
| 216 | QTextBlock block = pDocument->lastBlock();//findBlockByLineNumber(pDocument->lineCount()-1);
|
---|
| 217 | if (!block.isValid())
|
---|
| 218 | return QString();
|
---|
| 219 | QString lineStr = block.text();
|
---|
[71049] | 220 | if (lineStr.isNull() || lineStr.length() <= 1)
|
---|
[71025] | 221 | return QString();
|
---|
| 222 | /* Remove m_strPrompt from the line string: */
|
---|
| 223 | return (lineStr.right(lineStr.length()-m_strPrompt.length()));
|
---|
| 224 | }
|
---|
| 225 |
|
---|
| 226 | void UIGuestControlConsole::replaceLineContent(const QString &stringNewContent)
|
---|
| 227 | {
|
---|
[71066] | 228 | moveCursor(QTextCursor::End);
|
---|
[71025] | 229 | QTextCursor cursor = textCursor();
|
---|
| 230 | cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
|
---|
| 231 | cursor.removeSelectedText();
|
---|
| 232 |
|
---|
| 233 | QString newString(m_strPrompt);
|
---|
| 234 | newString.append(stringNewContent);
|
---|
| 235 | insertPlainText(newString);
|
---|
| 236 | moveCursor(QTextCursor::End);
|
---|
| 237 | }
|
---|
| 238 |
|
---|
| 239 | QString UIGuestControlConsole::getNextCommandFromHistory(const QString &originalString /* = QString() */)
|
---|
| 240 | {
|
---|
[71049] | 241 | if (m_tCommandHistory.empty())
|
---|
[71025] | 242 | return originalString;
|
---|
| 243 |
|
---|
[71049] | 244 | if (m_uCommandHistoryIndex == (unsigned)(m_tCommandHistory.size() - 1))
|
---|
[71025] | 245 | m_uCommandHistoryIndex = 0;
|
---|
| 246 | else
|
---|
| 247 | ++m_uCommandHistoryIndex;
|
---|
| 248 |
|
---|
| 249 | return m_tCommandHistory.at(m_uCommandHistoryIndex);
|
---|
| 250 | }
|
---|
| 251 |
|
---|
| 252 |
|
---|
| 253 | QString UIGuestControlConsole::getPreviousCommandFromHistory(const QString &originalString /* = QString() */)
|
---|
| 254 | {
|
---|
[71049] | 255 | if (m_tCommandHistory.empty())
|
---|
[71025] | 256 | return originalString;
|
---|
[71049] | 257 | if (m_uCommandHistoryIndex == 0)
|
---|
[71025] | 258 | m_uCommandHistoryIndex = m_tCommandHistory.size() - 1;
|
---|
| 259 | else
|
---|
| 260 | --m_uCommandHistoryIndex;
|
---|
| 261 |
|
---|
| 262 | return m_tCommandHistory.at(m_uCommandHistoryIndex);
|
---|
| 263 | }
|
---|
[71145] | 264 |
|
---|
| 265 | void UIGuestControlConsole::completeByTab()
|
---|
| 266 | {
|
---|
| 267 | bool lastLine = blockCount() == (textCursor().blockNumber() +1);
|
---|
| 268 | if (!lastLine)
|
---|
| 269 | return;
|
---|
| 270 | /* Save whatever we have currently on this line: */
|
---|
| 271 | QString currentCommand = getCommandString();
|
---|
| 272 |
|
---|
| 273 | QTextCursor cursor = textCursor();
|
---|
| 274 | /* Save the cursor's position within the line */
|
---|
| 275 | int cursorBlockPosition = cursor.positionInBlock();
|
---|
| 276 |
|
---|
| 277 | /* Find out on which word the cursor is. This is the word we will
|
---|
| 278 | complete: */
|
---|
| 279 | cursor.select(QTextCursor::WordUnderCursor);
|
---|
| 280 | QString currentWord = cursor.selectedText();
|
---|
| 281 |
|
---|
| 282 | const QList<QString> &matches = matchedWords(currentWord);
|
---|
| 283 | /* If there are no matches do nothing: */
|
---|
[71191] | 284 | if (matches.empty())
|
---|
[71145] | 285 | return;
|
---|
| 286 | /* if there are more than one match list them all and
|
---|
| 287 | reprint the line: */
|
---|
[71191] | 288 | if (matches.size() > 1)
|
---|
[71145] | 289 | {
|
---|
| 290 | moveCursor(QTextCursor::End);
|
---|
| 291 | QString strMatches;
|
---|
[71191] | 292 | for (int i = 0; i < matches.size(); ++i)
|
---|
[71145] | 293 | {
|
---|
| 294 | strMatches.append(matches.at(i));
|
---|
| 295 | strMatches.append(" ");
|
---|
| 296 | }
|
---|
| 297 | appendPlainText(strMatches);
|
---|
| 298 | insertPlainText(QString("\n").append(m_strPrompt));
|
---|
| 299 | insertPlainText(currentCommand);
|
---|
| 300 | /* Put the cursor in its previous position within the line: */
|
---|
| 301 | int blockPosition = textCursor().block().position();
|
---|
| 302 | QTextCursor nCursor = textCursor();
|
---|
| 303 | nCursor.setPosition(blockPosition + cursorBlockPosition);
|
---|
| 304 | setTextCursor(nCursor);
|
---|
| 305 | return;
|
---|
| 306 | }
|
---|
| 307 | /* if there is only one word just complete: */
|
---|
| 308 | /* some sanity checks */
|
---|
[71191] | 309 | if (matches.at(0).length() > currentWord.length())
|
---|
[71145] | 310 | insertPlainText(matches.at(0).right(matches.at(0).length() - currentWord.length()));
|
---|
| 311 | }
|
---|
| 312 |
|
---|
| 313 |
|
---|
| 314 | QList<QString> UIGuestControlConsole::matchedWords(const QString &strSearch) const
|
---|
| 315 | {
|
---|
| 316 | QList<QString> list;
|
---|
| 317 | /* Go thru the map and find which of its elements start with @pstrSearch: */
|
---|
[71191] | 318 | for (TabDictionary::const_iterator iterator = m_tabDictinary.begin();
|
---|
[71145] | 319 | iterator != m_tabDictinary.end(); ++iterator)
|
---|
| 320 | {
|
---|
| 321 | const QString &strMap = iterator.key();
|
---|
[71191] | 322 | if (strMap.startsWith(strSearch))
|
---|
[71145] | 323 | list.push_back(strMap);
|
---|
| 324 | }
|
---|
| 325 | return list;
|
---|
| 326 | }
|
---|