"Fossies" - the Fresh Open Source Software Archive

Member "cutter-1.8.2/src/widgets/ConsoleWidget.cpp" (20 May 2019, 9898 Bytes) of package /linux/privat/cutter-1.8.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ConsoleWidget.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.8.1_vs_1.8.2.

    1 #include <QScrollBar>
    2 #include <QMenu>
    3 #include <QCompleter>
    4 #include <QAction>
    5 #include <QShortcut>
    6 #include <QStringListModel>
    7 #include <QTimer>
    8 #include <QSettings>
    9 #include <iostream>
   10 #include "core/Cutter.h"
   11 #include "ConsoleWidget.h"
   12 #include "ui_ConsoleWidget.h"
   13 #include "common/Helpers.h"
   14 #include "common/SvgIconEngine.h"
   15 
   16 
   17 static const int invalidHistoryPos = -1;
   18 
   19 static const char *consoleWrapSettingsKey = "console.wrap";
   20 
   21 ConsoleWidget::ConsoleWidget(MainWindow *main, QAction *action) :
   22     CutterDockWidget(main, action),
   23     ui(new Ui::ConsoleWidget),
   24     debugOutputEnabled(true),
   25     maxHistoryEntries(100),
   26     lastHistoryPosition(invalidHistoryPos),
   27     completer(nullptr),
   28     historyUpShortcut(nullptr),
   29     historyDownShortcut(nullptr)
   30 {
   31     ui->setupUi(this);
   32 
   33     // Adjust console lineedit
   34     ui->inputLineEdit->setTextMargins(10, 0, 0, 0);
   35 
   36     setupFont();
   37 
   38     // Adjust text margins of consoleOutputTextEdit
   39     QTextDocument *console_docu = ui->outputTextEdit->document();
   40     console_docu->setDocumentMargin(10);
   41 
   42     QAction *actionClear = new QAction(tr("Clear Output"), ui->outputTextEdit);
   43     connect(actionClear, SIGNAL(triggered(bool)), ui->outputTextEdit, SLOT(clear()));
   44     actions.append(actionClear);
   45 
   46     actionWrapLines = new QAction(tr("Wrap Lines"), ui->outputTextEdit);
   47     actionWrapLines->setCheckable(true);
   48     setWrap(QSettings().value(consoleWrapSettingsKey, true).toBool());
   49     connect(actionWrapLines, &QAction::triggered, this, [this] (bool checked) {
   50         setWrap(checked);
   51     });
   52     actions.append(actionWrapLines);
   53 
   54     // Completion
   55     completionActive = false;
   56     completer = new QCompleter(&completionModel, this);
   57     completer->setMaxVisibleItems(20);
   58     completer->setCaseSensitivity(Qt::CaseInsensitive);
   59     completer->setFilterMode(Qt::MatchStartsWith);
   60     ui->inputLineEdit->setCompleter(completer);
   61 
   62     connect(ui->inputLineEdit, &QLineEdit::textEdited, this, &ConsoleWidget::updateCompletion);
   63     updateCompletion();
   64 
   65     // Set console output context menu
   66     ui->outputTextEdit->setContextMenuPolicy(Qt::CustomContextMenu);
   67     connect(ui->outputTextEdit, SIGNAL(customContextMenuRequested(const QPoint &)),
   68             this, SLOT(showCustomContextMenu(const QPoint &)));
   69 
   70     // Esc clears inputLineEdit (like OmniBar)
   71     QShortcut *clear_shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), ui->inputLineEdit);
   72     connect(clear_shortcut, SIGNAL(activated()), this, SLOT(clear()));
   73     clear_shortcut->setContext(Qt::WidgetShortcut);
   74 
   75     // Up and down arrows show history
   76     historyUpShortcut = new QShortcut(QKeySequence(Qt::Key_Up), ui->inputLineEdit);
   77     connect(historyUpShortcut, SIGNAL(activated()), this, SLOT(historyPrev()));
   78     historyUpShortcut->setContext(Qt::WidgetShortcut);
   79 
   80     historyDownShortcut = new QShortcut(QKeySequence(Qt::Key_Down), ui->inputLineEdit);
   81     connect(historyDownShortcut, SIGNAL(activated()), this, SLOT(historyNext()));
   82     historyDownShortcut->setContext(Qt::WidgetShortcut);
   83 
   84     QShortcut *completionShortcut = new QShortcut(QKeySequence(Qt::Key_Tab), ui->inputLineEdit);
   85     connect(completionShortcut, &QShortcut::activated, this, &ConsoleWidget::triggerCompletion);
   86 
   87     connect(ui->inputLineEdit, &QLineEdit::editingFinished, this, &ConsoleWidget::disableCompletion);
   88 
   89     connect(Config(), &Configuration::fontsUpdated, this, &ConsoleWidget::setupFont);
   90     connect(Config(), &Configuration::interfaceThemeChanged, this, &ConsoleWidget::setupFont);
   91 
   92     completer->popup()->installEventFilter(this);
   93 }
   94 
   95 ConsoleWidget::~ConsoleWidget()
   96 {
   97     delete completer;
   98 }
   99 
  100 bool ConsoleWidget::eventFilter(QObject *obj, QEvent *event)
  101 {
  102     if(completer && obj == completer->popup() &&
  103         // disable up/down shortcuts if completer is shown
  104         (event->type() == QEvent::Type::Show || event->type() == QEvent::Type::Hide)) {
  105         bool enabled = !completer->popup()->isVisible();
  106         if (historyUpShortcut) {
  107             historyUpShortcut->setEnabled(enabled);
  108         }
  109         if (historyDownShortcut) {
  110             historyDownShortcut->setEnabled(enabled);
  111         }
  112     }
  113     return false;
  114 }
  115 
  116 void ConsoleWidget::setupFont()
  117 {
  118     ui->outputTextEdit->setFont(Config()->getFont());
  119 }
  120 
  121 void ConsoleWidget::addOutput(const QString &msg)
  122 {
  123     ui->outputTextEdit->appendPlainText(msg);
  124     scrollOutputToEnd();
  125 }
  126 
  127 void ConsoleWidget::addDebugOutput(const QString &msg)
  128 {
  129     if (debugOutputEnabled) {
  130         ui->outputTextEdit->appendHtml("<font color=\"red\"> [DEBUG]:\t" + msg + "</font>");
  131         scrollOutputToEnd();
  132     }
  133 }
  134 
  135 void ConsoleWidget::focusInputLineEdit()
  136 {
  137     ui->inputLineEdit->setFocus();
  138 }
  139 
  140 void ConsoleWidget::removeLastLine()
  141 {
  142     ui->outputTextEdit->setFocus();
  143     QTextCursor cur = ui->outputTextEdit->textCursor();
  144     ui->outputTextEdit->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
  145     ui->outputTextEdit->moveCursor(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
  146     ui->outputTextEdit->moveCursor(QTextCursor::End, QTextCursor::KeepAnchor);
  147     ui->outputTextEdit->textCursor().removeSelectedText();
  148     ui->outputTextEdit->textCursor().deletePreviousChar();
  149     ui->outputTextEdit->setTextCursor(cur);
  150 }
  151 
  152 void ConsoleWidget::executeCommand(const QString &command)
  153 {
  154     if (!commandTask.isNull()) {
  155         return;
  156     }
  157     ui->inputLineEdit->setEnabled(false);
  158 
  159     const int originalLines = ui->outputTextEdit->blockCount();
  160     QTimer *timer = new QTimer(this);
  161     timer->setInterval(500);
  162     timer->setSingleShot(true);
  163     connect(timer, &QTimer::timeout, [this]() {
  164         ui->outputTextEdit->appendPlainText("Executing the command...");
  165     });
  166 
  167     QString cmd_line = "<br>[" + RAddressString(Core()->getOffset()) + "]> " + command + "<br>";
  168     RVA oldOffset = Core()->getOffset();
  169     commandTask = QSharedPointer<CommandTask>(new CommandTask(command, CommandTask::ColorMode::MODE_256, true));
  170     connect(commandTask.data(), &CommandTask::finished, this, [this, cmd_line,
  171           command, originalLines, oldOffset] (const QString & result) {
  172 
  173         if (originalLines < ui->outputTextEdit->blockCount()) {
  174             removeLastLine();
  175         }
  176         ui->outputTextEdit->appendHtml(cmd_line + result);
  177         scrollOutputToEnd();
  178         historyAdd(command);
  179         commandTask = nullptr;
  180         ui->inputLineEdit->setEnabled(true);
  181         ui->inputLineEdit->setFocus();
  182         if (oldOffset != Core()->getOffset()) {
  183             Core()->updateSeek();
  184         }
  185     });
  186     connect(commandTask.data(), &CommandTask::finished, timer, &QTimer::stop);
  187 
  188     timer->start();
  189     Core()->getAsyncTaskManager()->start(commandTask);
  190 }
  191 
  192 void ConsoleWidget::setWrap(bool wrap)
  193 {
  194     QSettings().setValue(consoleWrapSettingsKey, wrap);
  195     actionWrapLines->setChecked(wrap);
  196     ui->outputTextEdit->setLineWrapMode(wrap ? QPlainTextEdit::WidgetWidth: QPlainTextEdit::NoWrap);
  197 }
  198 
  199 void ConsoleWidget::on_inputLineEdit_returnPressed()
  200 {
  201     QString input = ui->inputLineEdit->text();
  202     if (input.isEmpty()) {
  203         return;
  204     }
  205     executeCommand(input);
  206     ui->inputLineEdit->clear();
  207 }
  208 
  209 void ConsoleWidget::on_execButton_clicked()
  210 {
  211     on_inputLineEdit_returnPressed();
  212 }
  213 
  214 void ConsoleWidget::showCustomContextMenu(const QPoint &pt)
  215 {
  216     actionWrapLines->setChecked(ui->outputTextEdit->lineWrapMode() == QPlainTextEdit::WidgetWidth);
  217 
  218     QMenu *menu = new QMenu(ui->outputTextEdit);
  219     menu->addActions(actions);
  220     menu->exec(ui->outputTextEdit->mapToGlobal(pt));
  221     menu->deleteLater();
  222 }
  223 
  224 void ConsoleWidget::historyNext()
  225 {
  226     if (!history.isEmpty()) {
  227         if (lastHistoryPosition > invalidHistoryPos) {
  228             if (lastHistoryPosition >= history.size()) {
  229                 lastHistoryPosition = history.size() - 1 ;
  230             }
  231 
  232             --lastHistoryPosition;
  233 
  234             if (lastHistoryPosition >= 0) {
  235                 ui->inputLineEdit->setText(history.at(lastHistoryPosition));
  236             } else {
  237                 ui->inputLineEdit->clear();
  238             }
  239 
  240 
  241         }
  242     }
  243 }
  244 
  245 void ConsoleWidget::historyPrev()
  246 {
  247     if (!history.isEmpty()) {
  248         if (lastHistoryPosition >= history.size() - 1) {
  249             lastHistoryPosition = history.size() - 2;
  250         }
  251 
  252         ui->inputLineEdit->setText(history.at(++lastHistoryPosition));
  253     }
  254 }
  255 
  256 void ConsoleWidget::triggerCompletion()
  257 {
  258     if (completionActive) {
  259         return;
  260     }
  261     completionActive = true;
  262     updateCompletion();
  263     completer->complete();
  264 }
  265 
  266 void ConsoleWidget::disableCompletion()
  267 {
  268     if (!completionActive) {
  269         return;
  270     }
  271     completionActive = false;
  272     updateCompletion();
  273     completer->popup()->hide();
  274 }
  275 
  276 void ConsoleWidget::updateCompletion()
  277 {
  278     if (!completionActive) {
  279         completionModel.setStringList({});
  280         return;
  281     }
  282 
  283     auto current = ui->inputLineEdit->text();
  284     auto completions = Core()->autocomplete(current, R_LINE_PROMPT_DEFAULT);
  285     int lastSpace = current.lastIndexOf(' ');
  286     if (lastSpace >= 0) {
  287         current = current.left(lastSpace + 1);
  288         for (auto &s : completions) {
  289             s = current + s;
  290         }
  291     }
  292     completionModel.setStringList(completions);
  293 }
  294 
  295 void ConsoleWidget::clear()
  296 {
  297     disableCompletion();
  298     ui->inputLineEdit->clear();
  299 
  300     invalidateHistoryPosition();
  301 
  302     // Close the potential shown completer popup
  303     ui->inputLineEdit->clearFocus();
  304     ui->inputLineEdit->setFocus();
  305 }
  306 
  307 void ConsoleWidget::scrollOutputToEnd()
  308 {
  309     const int maxValue = ui->outputTextEdit->verticalScrollBar()->maximum();
  310     ui->outputTextEdit->verticalScrollBar()->setValue(maxValue);
  311 }
  312 
  313 void ConsoleWidget::historyAdd(const QString &input)
  314 {
  315     if (history.size() + 1 > maxHistoryEntries) {
  316         history.removeLast();
  317     }
  318 
  319     history.prepend(input);
  320 
  321     invalidateHistoryPosition();
  322 }
  323 void ConsoleWidget::invalidateHistoryPosition()
  324 {
  325     lastHistoryPosition = invalidHistoryPos;
  326 }