Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/ElmerGUI/Application/vtkpost/ecmaconsole.cpp
3203 views
1
/*****************************************************************************
2
* *
3
* Elmer, A Finite Element Software for Multiphysical Problems *
4
* *
5
* Copyright 1st April 1995 - , CSC - IT Center for Science Ltd., Finland *
6
* *
7
* This program is free software; you can redistribute it and/or *
8
* modify it under the terms of the GNU General Public License *
9
* as published by the Free Software Foundation; either version 2 *
10
* of the License, or (at your option) any later version. *
11
* *
12
* This program is distributed in the hope that it will be useful, *
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15
* GNU General Public License for more details. *
16
* *
17
* You should have received a copy of the GNU General Public License *
18
* along with this program (in file fem/GPL-2); if not, write to the *
19
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
20
* Boston, MA 02110-1301, USA. *
21
* *
22
*****************************************************************************/
23
24
/*****************************************************************************
25
* *
26
* ElmerGUI ecmaconsole *
27
* *
28
* Modified from the PythonQt console by Florian Link / MeVis Research *
29
* *
30
*****************************************************************************
31
* *
32
* Authors: Mikko Lyly, Juha Ruokolainen and Peter RÃ¥back *
33
* Email: [email protected] *
34
* Web: http://www.csc.fi/elmer *
35
* Address: CSC - IT Center for Science Ltd. *
36
* Keilaranta 14 *
37
* 02101 Espoo, Finland *
38
* *
39
* Original Date: 15 Mar 2008 *
40
* *
41
*****************************************************************************/
42
43
#include "ecmaconsole.h"
44
45
#if WITH_QT5 || WITH_QT6
46
#include <QtWidgets>
47
#endif
48
#include <QWidget>
49
#include <QKeyEvent>
50
#include <QMouseEvent>
51
#include <QTextCursor>
52
#include <QTextBlock>
53
#include <QMetaObject>
54
#include <QMetaMethod>
55
#include <QCompleter>
56
#include <QStringListModel>
57
#include <QScrollBar>
58
59
#if WITH_QT6
60
#include <QJSEngine>
61
#endif
62
63
#include <iostream>
64
using namespace std;
65
66
EcmaConsole::EcmaConsole(QWidget* parent)
67
: QTextEdit(parent)
68
{
69
prompt = "qs> ";
70
this->clearHistory();
71
}
72
73
EcmaConsole::~EcmaConsole()
74
{
75
}
76
77
void EcmaConsole::mouseDoubleClickEvent(QMouseEvent* event)
78
{
79
event->ignore();
80
}
81
82
void EcmaConsole::mousePressEvent(QMouseEvent* event)
83
{
84
event->ignore();
85
}
86
87
void EcmaConsole::mouseReleaseEvent(QMouseEvent* event)
88
{
89
event->ignore();
90
}
91
92
void EcmaConsole::keyPressEvent(QKeyEvent* event)
93
{
94
if(completer && completer->popup()->isVisible()) {
95
switch(event->key()) {
96
case Qt::Key_Return:
97
if(!completer->popup()->currentIndex().isValid()) {
98
insertCompletion(completer->currentCompletion());
99
completer->popup()->hide();
100
event->accept();
101
return;
102
}
103
event->ignore();
104
return;
105
case Qt::Key_Enter:
106
case Qt::Key_Escape:
107
case Qt::Key_Tab:
108
case Qt::Key_Backtab:
109
event->ignore();
110
return;
111
default:
112
break;
113
}
114
}
115
116
bool eventHandled = false;
117
118
switch(event->key()) {
119
case Qt::Key_Return:
120
execLine();
121
eventHandled = true;
122
break;
123
124
case Qt::Key_Up:
125
if(historyPtr > 0) {
126
historyPtr--;
127
scanHistory();
128
}
129
eventHandled = true;
130
break;
131
132
case Qt::Key_Down:
133
if(historyPtr < history.count()-1) {
134
historyPtr++;
135
scanHistory();
136
}
137
eventHandled = true;
138
break;
139
140
case Qt::Key_Left:
141
case Qt::Key_Backspace:
142
case Qt::Key_Backtab:
143
if(this->textCursor().position() <= getPromptPos())
144
eventHandled = true;
145
break;
146
147
default:
148
break;
149
}
150
151
if(eventHandled) {
152
completer->popup()->hide();
153
event->ignore();
154
} else {
155
QTextEdit::keyPressEvent(event);
156
QString text = event->text();
157
if(!text.isEmpty()) {
158
handleTabCompletion();
159
} else {
160
completer->popup()->hide();
161
}
162
}
163
}
164
165
int EcmaConsole::getPromptPos()
166
{
167
QTextCursor textCursor(this->textCursor());
168
textCursor.movePosition(QTextCursor::End);
169
int position = textCursor.block().position() + prompt.length();
170
return position;
171
}
172
173
void EcmaConsole::execLine()
174
{
175
QTextCursor textCursor = this->textCursor();
176
textCursor.movePosition(QTextCursor::End);
177
textCursor.setPosition(getPromptPos());
178
textCursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
179
QString line = textCursor.selectedText().trimmed();
180
181
if(!line.isEmpty()) {
182
emit(cmd(line));
183
history << line;
184
historyPtr = history.count();
185
}
186
187
this->append(prompt);
188
textCursor = this->textCursor();
189
textCursor.movePosition(QTextCursor::End);
190
setTextCursor(textCursor);
191
}
192
193
void EcmaConsole::scanHistory()
194
{
195
QTextCursor textCursor = this->textCursor();
196
textCursor.movePosition(QTextCursor::End);
197
textCursor.setPosition(getPromptPos(), QTextCursor::KeepAnchor);
198
textCursor.insertText(history.value(historyPtr));
199
textCursor.movePosition(QTextCursor::End);
200
setTextCursor(textCursor);
201
}
202
203
void EcmaConsole::clearHistory()
204
{
205
this->clear();
206
history.clear();
207
historyPtr = 0;
208
this->append(prompt);
209
}
210
211
void EcmaConsole::addNames(QString className, const QMetaObject* metaObject)
212
{
213
QStringList publicSlots;
214
int methodCount = metaObject->methodCount();
215
for(int i = 0; i < methodCount; i++) {
216
QMetaMethod method = metaObject->method(i);
217
QMetaMethod::Access access = method.access();
218
QMetaMethod::MethodType methodType = method.methodType();
219
if((access == QMetaMethod::Public) && (methodType == QMetaMethod::Slot)) {
220
221
#if WITH_QT5 || WITH_QT6
222
QString signature = method.methodSignature();
223
#else
224
QString signature = method.signature();
225
#endif
226
int j = signature.indexOf("(");
227
QString slotName = signature.left(j);
228
publicSlots << slotName;
229
}
230
}
231
publicSlots.sort();
232
names.insert(className, publicSlots);
233
}
234
235
void EcmaConsole::initCompleter()
236
{
237
completer = new QCompleter(this);
238
completer->setWidget(this);
239
connect(completer, SIGNAL(activated(const QString&)), this, SLOT(insertCompletion(const QString&)));
240
}
241
242
243
void EcmaConsole::handleTabCompletion()
244
{
245
QTextCursor textCursor = this->textCursor();
246
int pos = textCursor.position();
247
textCursor.setPosition(getPromptPos());
248
textCursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
249
int startPos = textCursor.selectionStart();
250
251
int offset = pos-startPos;
252
QString text = textCursor.selectedText();
253
254
QString textToComplete;
255
int cur = offset;
256
257
while(cur--) {
258
QChar c = text.at(cur);
259
if(c.isLetterOrNumber() || (c == '.') || (c == '_')) {
260
textToComplete.prepend(c);
261
} else {
262
break;
263
}
264
}
265
266
QString lookup;
267
QString compareText = textToComplete;
268
int dot = compareText.lastIndexOf('.');
269
270
if(dot != -1) {
271
lookup = compareText.mid(0, dot);
272
compareText = compareText.mid(dot+1, offset);
273
}
274
275
if(!lookup.isEmpty() || !compareText.isEmpty()) {
276
compareText = compareText.toLower();
277
QStringList found;
278
279
QStringList list;
280
if(lookup.isEmpty()) {
281
// all class names
282
list = names.keys();
283
} else {
284
// methods for a class
285
list = names.value(lookup);
286
}
287
288
foreach(QString name, list) {
289
if(name.toLower().startsWith(compareText))
290
found << name;
291
}
292
293
if(!found.isEmpty()) {
294
completer->setCompletionPrefix(compareText);
295
completer->setCompletionMode(QCompleter::PopupCompletion);
296
completer->setModel(new QStringListModel(found, completer));
297
completer->setCaseSensitivity(Qt::CaseInsensitive);
298
QTextCursor c = this->textCursor();
299
c.movePosition(QTextCursor::StartOfWord);
300
QRect cr = cursorRect(c);
301
cr.setWidth(completer->popup()->sizeHintForColumn(0)
302
+ completer->popup()->verticalScrollBar()->sizeHint().width());
303
cr.translate(0,8);
304
completer->complete(cr);
305
} else {
306
completer->popup()->hide();
307
}
308
} else {
309
completer->popup()->hide();
310
}
311
}
312
313
void EcmaConsole::insertCompletion(const QString& completion)
314
{
315
QTextCursor tc = textCursor();
316
tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor);
317
if (tc.selectedText() == ".") {
318
tc.insertText(QString(".") + completion);
319
} else {
320
tc = textCursor();
321
tc.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
322
tc.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
323
tc.insertText(completion);
324
setTextCursor(tc);
325
}
326
}
327
328
#if WITH_QT6
329
template<class... A> QJSValue EcmaConsole::print(A... args)
330
{
331
QString result;
332
333
for(char* s : std::initializer_list<char*>{args...}) {
334
result.append(" ");
335
result.append(s);
336
}
337
append(result.trimmed());
338
339
return QJSValue(QJSValue::UndefinedValue);
340
}
341
#endif
342