/src/scripttools/debugging/qscriptdebuggerconsolewidget.cpp

https://bitbucket.org/ultra_iter/qt-vtl · C++ · 444 lines · 353 code · 44 blank · 47 comment · 50 complexity · bf878af1a28d4216a01f42f1a4ff0885 MD5 · raw file

  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
  4. ** All rights reserved.
  5. ** Contact: Nokia Corporation (qt-info@nokia.com)
  6. **
  7. ** This file is part of the QtSCriptTools module of the Qt Toolkit.
  8. **
  9. ** $QT_BEGIN_LICENSE:LGPL$
  10. ** GNU Lesser General Public License Usage
  11. ** This file may be used under the terms of the GNU Lesser General Public
  12. ** License version 2.1 as published by the Free Software Foundation and
  13. ** appearing in the file LICENSE.LGPL included in the packaging of this
  14. ** file. Please review the following information to ensure the GNU Lesser
  15. ** General Public License version 2.1 requirements will be met:
  16. ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
  17. **
  18. ** In addition, as a special exception, Nokia gives you certain additional
  19. ** rights. These rights are described in the Nokia Qt LGPL Exception
  20. ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
  21. **
  22. ** GNU General Public License Usage
  23. ** Alternatively, this file may be used under the terms of the GNU General
  24. ** Public License version 3.0 as published by the Free Software Foundation
  25. ** and appearing in the file LICENSE.GPL included in the packaging of this
  26. ** file. Please review the following information to ensure the GNU General
  27. ** Public License version 3.0 requirements will be met:
  28. ** http://www.gnu.org/copyleft/gpl.html.
  29. **
  30. ** Other Usage
  31. ** Alternatively, this file may be used in accordance with the terms and
  32. ** conditions contained in a signed written agreement between you and Nokia.
  33. **
  34. **
  35. **
  36. **
  37. **
  38. ** $QT_END_LICENSE$
  39. **
  40. ****************************************************************************/
  41. #include "qscriptdebuggerconsolewidget_p.h"
  42. #include "qscriptdebuggerconsolewidgetinterface_p_p.h"
  43. #include "qscriptdebuggerconsolehistorianinterface_p.h"
  44. #include "qscriptcompletionproviderinterface_p.h"
  45. #include "qscriptcompletiontaskinterface_p.h"
  46. #include <QtCore/qdebug.h>
  47. #include <QtGui/qplaintextedit.h>
  48. #include <QtGui/qlabel.h>
  49. #include <QtGui/qlineedit.h>
  50. #include <QtGui/qlistview.h>
  51. #include <QtGui/qscrollbar.h>
  52. #include <QtGui/qboxlayout.h>
  53. #include <QtGui/qcompleter.h>
  54. QT_BEGIN_NAMESPACE
  55. namespace {
  56. class PromptLabel : public QLabel
  57. {
  58. public:
  59. PromptLabel(QWidget *parent = 0)
  60. : QLabel(parent)
  61. {
  62. setFrameShape(QFrame::NoFrame);
  63. setIndent(2);
  64. setMargin(2);
  65. setSizePolicy(QSizePolicy::Minimum, sizePolicy().verticalPolicy());
  66. setAlignment(Qt::AlignHCenter);
  67. #ifndef QT_NO_STYLE_STYLESHEET
  68. setStyleSheet(QLatin1String("background: white;"));
  69. #endif
  70. }
  71. QSize sizeHint() const {
  72. QFontMetrics fm(font());
  73. return fm.size(0, text()) + QSize(8, 0);
  74. }
  75. };
  76. class InputEdit : public QLineEdit
  77. {
  78. public:
  79. InputEdit(QWidget *parent = 0)
  80. : QLineEdit(parent)
  81. {
  82. setFrame(false);
  83. setSizePolicy(QSizePolicy::MinimumExpanding, sizePolicy().verticalPolicy());
  84. }
  85. };
  86. class CommandLine : public QWidget
  87. {
  88. Q_OBJECT
  89. public:
  90. CommandLine(QWidget *parent = 0)
  91. : QWidget(parent)
  92. {
  93. promptLabel = new PromptLabel();
  94. inputEdit = new InputEdit();
  95. QHBoxLayout *hbox = new QHBoxLayout(this);
  96. hbox->setSpacing(0);
  97. hbox->setMargin(0);
  98. hbox->addWidget(promptLabel);
  99. hbox->addWidget(inputEdit);
  100. QObject::connect(inputEdit, SIGNAL(returnPressed()),
  101. this, SLOT(onReturnPressed()));
  102. QObject::connect(inputEdit, SIGNAL(textEdited(QString)),
  103. this, SIGNAL(lineEdited(QString)));
  104. setFocusProxy(inputEdit);
  105. }
  106. QString prompt() const
  107. {
  108. return promptLabel->text();
  109. }
  110. void setPrompt(const QString &prompt)
  111. {
  112. promptLabel->setText(prompt);
  113. }
  114. QString input() const
  115. {
  116. return inputEdit->text();
  117. }
  118. void setInput(const QString &input)
  119. {
  120. inputEdit->setText(input);
  121. }
  122. int cursorPosition() const
  123. {
  124. return inputEdit->cursorPosition();
  125. }
  126. void setCursorPosition(int position)
  127. {
  128. inputEdit->setCursorPosition(position);
  129. }
  130. QWidget *editor() const
  131. {
  132. return inputEdit;
  133. }
  134. Q_SIGNALS:
  135. void lineEntered(const QString &contents);
  136. void lineEdited(const QString &contents);
  137. private Q_SLOTS:
  138. void onReturnPressed()
  139. {
  140. QString text = inputEdit->text();
  141. inputEdit->clear();
  142. emit lineEntered(text);
  143. }
  144. private:
  145. PromptLabel *promptLabel;
  146. InputEdit *inputEdit;
  147. };
  148. class QScriptDebuggerConsoleWidgetOutputEdit : public QPlainTextEdit
  149. {
  150. public:
  151. QScriptDebuggerConsoleWidgetOutputEdit(QWidget *parent = 0)
  152. : QPlainTextEdit(parent)
  153. {
  154. setFrameShape(QFrame::NoFrame);
  155. setReadOnly(true);
  156. // ### there's no context menu when the edit can't have focus,
  157. // even though you can select text in it.
  158. // setFocusPolicy(Qt::NoFocus);
  159. setMaximumBlockCount(2500);
  160. }
  161. void scrollToBottom()
  162. {
  163. QScrollBar *bar = verticalScrollBar();
  164. bar->setValue(bar->maximum());
  165. }
  166. int charactersPerLine() const
  167. {
  168. QFontMetrics fm(font());
  169. return width() / fm.maxWidth();
  170. }
  171. };
  172. } // namespace
  173. class QScriptDebuggerConsoleWidgetPrivate
  174. : public QScriptDebuggerConsoleWidgetInterfacePrivate
  175. {
  176. Q_DECLARE_PUBLIC(QScriptDebuggerConsoleWidget)
  177. public:
  178. QScriptDebuggerConsoleWidgetPrivate();
  179. ~QScriptDebuggerConsoleWidgetPrivate();
  180. // private slots
  181. void _q_onLineEntered(const QString &contents);
  182. void _q_onLineEdited(const QString &contents);
  183. void _q_onCompletionTaskFinished();
  184. CommandLine *commandLine;
  185. QScriptDebuggerConsoleWidgetOutputEdit *outputEdit;
  186. int historyIndex;
  187. QString newInput;
  188. };
  189. QScriptDebuggerConsoleWidgetPrivate::QScriptDebuggerConsoleWidgetPrivate()
  190. {
  191. historyIndex = -1;
  192. }
  193. QScriptDebuggerConsoleWidgetPrivate::~QScriptDebuggerConsoleWidgetPrivate()
  194. {
  195. }
  196. void QScriptDebuggerConsoleWidgetPrivate::_q_onLineEntered(const QString &contents)
  197. {
  198. Q_Q(QScriptDebuggerConsoleWidget);
  199. outputEdit->appendPlainText(QString::fromLatin1("%0 %1").arg(commandLine->prompt()).arg(contents));
  200. outputEdit->scrollToBottom();
  201. historyIndex = -1;
  202. newInput.clear();
  203. emit q->lineEntered(contents);
  204. }
  205. void QScriptDebuggerConsoleWidgetPrivate::_q_onLineEdited(const QString &contents)
  206. {
  207. if (historyIndex != -1) {
  208. // ### try to get the bash behavior...
  209. #if 0
  210. historian->changeHistoryAt(historyIndex, contents);
  211. #endif
  212. } else {
  213. newInput = contents;
  214. }
  215. }
  216. static bool lengthLessThan(const QString &s1, const QString &s2)
  217. {
  218. return s1.length() < s2.length();
  219. }
  220. // input must be sorted by length already
  221. static QString longestCommonPrefix(const QStringList &lst)
  222. {
  223. QString result = lst.last();
  224. for (int i = lst.size() - 2; (i >= 0) && !result.isEmpty(); --i) {
  225. const QString &s = lst.at(i);
  226. int j = 0;
  227. for ( ; (j < qMin(s.length(), result.length())) && (s.at(j) == result.at(j)); ++j)
  228. ;
  229. result = result.left(j);
  230. }
  231. return result;
  232. }
  233. void QScriptDebuggerConsoleWidgetPrivate::_q_onCompletionTaskFinished()
  234. {
  235. QScriptCompletionTaskInterface *task = 0;
  236. task = qobject_cast<QScriptCompletionTaskInterface*>(q_func()->sender());
  237. if (task->resultCount() == 1) {
  238. QString completion = task->resultAt(0);
  239. completion.append(task->appendix());
  240. QString tmp = commandLine->input();
  241. tmp.remove(task->position(), task->length());
  242. tmp.insert(task->position(), completion);
  243. commandLine->setInput(tmp);
  244. } else if (task->resultCount() > 1) {
  245. {
  246. QStringList lst;
  247. for (int i = 0; i < task->resultCount(); ++i)
  248. lst.append(task->resultAt(i).mid(task->length()));
  249. qSort(lst.begin(), lst.end(), lengthLessThan);
  250. QString lcp = longestCommonPrefix(lst);
  251. if (!lcp.isEmpty()) {
  252. QString tmp = commandLine->input();
  253. tmp.insert(task->position() + task->length(), lcp);
  254. commandLine->setInput(tmp);
  255. }
  256. }
  257. outputEdit->appendPlainText(QString::fromLatin1("%0 %1")
  258. .arg(commandLine->prompt()).arg(commandLine->input()));
  259. int maxLength = 0;
  260. for (int i = 0; i < task->resultCount(); ++i)
  261. maxLength = qMax(maxLength, task->resultAt(i).length());
  262. Q_ASSERT(maxLength > 0);
  263. int tab = 8;
  264. int columns = qMax(1, outputEdit->charactersPerLine() / (maxLength + tab));
  265. QString msg;
  266. for (int i = 0; i < task->resultCount(); ++i) {
  267. if (i != 0) {
  268. if ((i % columns) == 0) {
  269. outputEdit->appendPlainText(msg);
  270. msg.clear();
  271. } else {
  272. int pad = maxLength + tab - (msg.length() % (maxLength + tab));
  273. msg.append(QString(pad, QLatin1Char(' ')));
  274. }
  275. }
  276. msg.append(task->resultAt(i));
  277. }
  278. if (!msg.isEmpty())
  279. outputEdit->appendPlainText(msg);
  280. outputEdit->scrollToBottom();
  281. }
  282. task->deleteLater();
  283. }
  284. QScriptDebuggerConsoleWidget::QScriptDebuggerConsoleWidget(QWidget *parent)
  285. : QScriptDebuggerConsoleWidgetInterface(*new QScriptDebuggerConsoleWidgetPrivate, parent, 0)
  286. {
  287. Q_D(QScriptDebuggerConsoleWidget);
  288. d->commandLine = new CommandLine();
  289. d->commandLine->setPrompt(QString::fromLatin1("qsdb>"));
  290. d->outputEdit = new QScriptDebuggerConsoleWidgetOutputEdit();
  291. QVBoxLayout *vbox = new QVBoxLayout(this);
  292. vbox->setSpacing(0);
  293. vbox->setMargin(0);
  294. vbox->addWidget(d->outputEdit);
  295. vbox->addWidget(d->commandLine);
  296. #if 0
  297. QString sheet = QString::fromLatin1("background-color: black;"
  298. "color: aquamarine;"
  299. "font-size: 14px;"
  300. "font-family: \"Monospace\"");
  301. #endif
  302. #ifndef QT_NO_STYLE_STYLESHEET
  303. QString sheet = QString::fromLatin1("font-size: 14px; font-family: \"Monospace\";");
  304. setStyleSheet(sheet);
  305. #endif
  306. QObject::connect(d->commandLine, SIGNAL(lineEntered(QString)),
  307. this, SLOT(_q_onLineEntered(QString)));
  308. QObject::connect(d->commandLine, SIGNAL(lineEdited(QString)),
  309. this, SLOT(_q_onLineEdited(QString)));
  310. }
  311. QScriptDebuggerConsoleWidget::~QScriptDebuggerConsoleWidget()
  312. {
  313. }
  314. void QScriptDebuggerConsoleWidget::message(
  315. QtMsgType type, const QString &text, const QString &fileName,
  316. int lineNumber, int columnNumber, const QVariant &/*data*/)
  317. {
  318. Q_D(QScriptDebuggerConsoleWidget);
  319. QString msg;
  320. if (!fileName.isEmpty() || (lineNumber != -1)) {
  321. if (!fileName.isEmpty())
  322. msg.append(fileName);
  323. else
  324. msg.append(QLatin1String("<noname>"));
  325. if (lineNumber != -1) {
  326. msg.append(QLatin1Char(':'));
  327. msg.append(QString::number(lineNumber));
  328. if (columnNumber != -1) {
  329. msg.append(QLatin1Char(':'));
  330. msg.append(QString::number(columnNumber));
  331. }
  332. }
  333. msg.append(QLatin1String(": "));
  334. }
  335. msg.append(text);
  336. QTextCharFormat oldFmt = d->outputEdit->currentCharFormat();
  337. QTextCharFormat fmt(oldFmt);
  338. if (type == QtCriticalMsg) {
  339. fmt.setForeground(Qt::red);
  340. d->outputEdit->setCurrentCharFormat(fmt);
  341. }
  342. d->outputEdit->appendPlainText(msg);
  343. d->outputEdit->setCurrentCharFormat(oldFmt);
  344. d->outputEdit->scrollToBottom();
  345. }
  346. void QScriptDebuggerConsoleWidget::setLineContinuationMode(bool enabled)
  347. {
  348. Q_D(QScriptDebuggerConsoleWidget);
  349. QString prompt = enabled
  350. ? QString::fromLatin1("....")
  351. : QString::fromLatin1("qsdb>");
  352. d->commandLine->setPrompt(prompt);
  353. }
  354. void QScriptDebuggerConsoleWidget::clear()
  355. {
  356. Q_D(QScriptDebuggerConsoleWidget);
  357. d->outputEdit->clear();
  358. }
  359. void QScriptDebuggerConsoleWidget::keyPressEvent(QKeyEvent *event)
  360. {
  361. Q_D(QScriptDebuggerConsoleWidget);
  362. if (event->key() == Qt::Key_Up) {
  363. if (d->historyIndex+1 == d->historian->historyCount())
  364. return;
  365. QString cmd = d->historian->historyAt(++d->historyIndex);
  366. d->commandLine->setInput(cmd);
  367. } else if (event->key() == Qt::Key_Down) {
  368. if (d->historyIndex == -1) {
  369. // nothing to do
  370. } else if (d->historyIndex == 0) {
  371. d->commandLine->setInput(d->newInput);
  372. --d->historyIndex;
  373. } else {
  374. QString cmd = d->historian->historyAt(--d->historyIndex);
  375. d->commandLine->setInput(cmd);
  376. }
  377. } else if (event->key() == Qt::Key_Tab) {
  378. QScriptCompletionTaskInterface *task = 0;
  379. task = d->completionProvider->createCompletionTask(
  380. d->commandLine->input(), d->commandLine->cursorPosition(),
  381. /*frameIndex=*/-1, // current frame
  382. QScriptCompletionProviderInterface::ConsoleCommandCompletion);
  383. QObject::connect(task, SIGNAL(finished()),
  384. this, SLOT(_q_onCompletionTaskFinished()));
  385. task->start();
  386. } else {
  387. QScriptDebuggerConsoleWidgetInterface::keyPressEvent(event);
  388. }
  389. }
  390. bool QScriptDebuggerConsoleWidget::focusNextPrevChild(bool b)
  391. {
  392. Q_D(QScriptDebuggerConsoleWidget);
  393. if (d->outputEdit->hasFocus())
  394. return QScriptDebuggerConsoleWidgetInterface::focusNextPrevChild(b);
  395. else
  396. return false;
  397. }
  398. QT_END_NAMESPACE
  399. #include "qscriptdebuggerconsolewidget.moc"
  400. #include "moc_qscriptdebuggerconsolewidget_p.cpp"