1
/***************************************************************************
4
--------------------------------------------------------------------
5
Copyright : (C) 2006 by Ion Vasilief,
8
Email (use @ for *) : ion_vasilief*yahoo.fr, thzs*gmx.net,
10
Description : Editor widget for scripting code
12
***************************************************************************/
14
/***************************************************************************
16
* This program is free software; you can redistribute it and/or modify *
17
* it under the terms of the GNU General Public License as published by *
18
* the Free Software Foundation; either version 2 of the License, or *
19
* (at your option) any later version. *
21
* This program is distributed in the hope that it will be useful, *
22
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
23
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
24
* GNU General Public License for more details. *
26
* You should have received a copy of the GNU General Public License *
27
* along with this program; if not, write to the Free Software *
28
* Foundation, Inc., 51 Franklin Street, Fifth Floor, *
29
* Boston, MA 02110-1301 USA *
31
***************************************************************************/
32
#include "ScriptEdit.h"
37
#include <QPrintDialog>
39
#include <QMessageBox>
40
#include <QFileDialog>
41
#include <QTextStream>
43
ScriptEdit::ScriptEdit(ScriptingEnv *env, QWidget *parent, const char *name)
44
: QTextEdit(parent, name), scripted(env), d_error(false)
46
myScript = scriptEnv->newScript("", this, name);
47
connect(myScript, SIGNAL(error(const QString&,const QString&,int)), this, SLOT(insertErrorMsg(const QString&)));
48
connect(myScript, SIGNAL(print(const QString&)), this, SLOT(scriptPrint(const QString&)));
50
setLineWrapMode(NoWrap);
51
setTextFormat(Qt::PlainText);
52
setAcceptRichText (false);
53
setFamily("Monospace");
55
d_fmt_default.setBackground(palette().brush(QPalette::Base));
56
d_fmt_success.setBackground(QBrush(QColor(128, 255, 128)));
57
d_fmt_failure.setBackground(QBrush(QColor(255,128,128)));
59
printCursor = textCursor();
61
actionExecute = new QAction(tr("E&xecute"), this);
62
actionExecute->setShortcut( tr("Ctrl+J") );
63
connect(actionExecute, SIGNAL(activated()), this, SLOT(execute()));
65
actionExecuteAll = new QAction(tr("Execute &All"), this);
66
actionExecuteAll->setShortcut( tr("Ctrl+Shift+J") );
67
connect(actionExecuteAll, SIGNAL(activated()), this, SLOT(executeAll()));
69
actionEval = new QAction(tr("&Evaluate Expression"), this);
70
actionEval->setShortcut( tr("Ctrl+Return") );
71
connect(actionEval, SIGNAL(activated()), this, SLOT(evaluate()));
73
actionPrint = new QAction(tr("&Print"), this);
74
connect(actionPrint, SIGNAL(activated()), this, SLOT(print()));
76
actionImport = new QAction(tr("&Import"), this);
77
connect(actionImport, SIGNAL(activated()), this, SLOT(importASCII()));
79
actionExport = new QAction(tr("&Export"), this);
80
connect(actionExport, SIGNAL(activated()), this, SLOT(exportASCII()));
82
functionsMenu = new QMenu(this);
83
Q_CHECK_PTR(functionsMenu);
84
connect(functionsMenu, SIGNAL(triggered(QAction *)), this, SLOT(insertFunction(QAction *)));
86
connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(handleContentsChange(int,int,int)));
89
void ScriptEdit::customEvent(QEvent *e)
91
if (e->type() == SCRIPTING_CHANGE_EVENT)
93
scriptingChangeEvent((ScriptingChangeEvent*)e);
95
myScript = scriptEnv->newScript("", this, name());
96
connect(myScript, SIGNAL(error(const QString&,const QString&,int)), this, SLOT(insertErrorMsg(const QString&)));
97
connect(myScript, SIGNAL(print(const QString&)), this, SLOT(scriptPrint(const QString&)));
101
void ScriptEdit::keyPressEvent(QKeyEvent *e)
103
QTextEdit::keyPressEvent(e);
104
if (e->key() == Qt::Key_Return)
108
void ScriptEdit::contextMenuEvent(QContextMenuEvent *e)
110
QMenu *menu = createStandardContextMenu();
113
menu->addAction(actionPrint);
114
menu->addAction(actionImport);
115
menu->addAction(actionExport);
116
menu->addSeparator();
118
menu->addAction(actionExecute);
119
menu->addAction(actionExecuteAll);
120
menu->addAction(actionEval);
122
if (parent()->inherits("Note"))
124
Note *sp = (Note*) parent();
125
QAction *actionAutoexec = new QAction( tr("Auto&exec"), menu );
126
actionAutoexec->setToggleAction(true);
127
actionAutoexec->setOn(sp->autoexec());
128
connect(actionAutoexec, SIGNAL(toggled(bool)), sp, SLOT(setAutoexec(bool)));
129
menu->addAction(actionAutoexec);
132
functionsMenu->clear();
133
functionsMenu->setTearOffEnabled(true);
134
QStringList flist = scriptEnv->mathFunctions();
136
for (int i=0; i<flist.size(); i++)
140
// group by text before "_" (would make sense if we renamed several functions...)
141
/*if (flist[i].contains("_") || (i<flist.size()-1 && flist[i+1].split("_")[0]==flist[i]))
142
menupart = flist[i].split("_")[0];
145
// group by first letter, avoiding submenus with only one entry
146
if ((i==0 || flist[i-1][0] != flist[i][0]) && (i==flist.size()-1 || flist[i+1][0] != flist[i][0]))
149
menupart = flist[i].left(1);
150
if (!menupart.isEmpty()) {
151
if (!submenu || menupart != submenu->title())
152
submenu = functionsMenu->addMenu(menupart);
153
newAction = submenu->addAction(flist[i]);
155
newAction = functionsMenu->addAction(flist[i]);
156
newAction->setData(i);
157
newAction->setWhatsThis(scriptEnv->mathFunctionDoc(flist[i]));
159
functionsMenu->setTitle(tr("&Functions"));
160
menu->addMenu(functionsMenu);
162
menu->exec(e->globalPos());
166
void ScriptEdit::insertErrorMsg(const QString &message)
168
QString err = message;
169
err.prepend("\n").replace("\n","\n#> ");
170
int start = printCursor.position();
171
printCursor.insertText(err);
172
printCursor.setPosition(start, QTextCursor::KeepAnchor);
173
setTextCursor(printCursor);
177
void ScriptEdit::scriptPrint(const QString &text)
179
if(lineNumber(printCursor.position()) == lineNumber(textCursor().selectionEnd()))
180
printCursor.insertText("\n");
181
printCursor.insertText(text);
184
void ScriptEdit::insertFunction(const QString &fname)
186
QTextCursor cursor = textCursor();
187
QString markedText = cursor.selectedText();
188
cursor.insertText(fname+"("+markedText+")");
189
if(markedText.isEmpty()){
190
// if no text is selected, place cursor inside the ()
191
// instead of after it
192
cursor.movePosition(QTextCursor::PreviousCharacter,QTextCursor::MoveAnchor,1);
193
// the next line makes the selection visible to the user
194
// (the line above only changes the selection in the
195
// underlying QTextDocument)
196
setTextCursor(cursor);
200
void ScriptEdit::insertFunction(QAction *action)
202
insertFunction(scriptEnv->mathFunctions()[action->data().toInt()]);
205
int ScriptEdit::lineNumber(int pos) const
208
for(QTextBlock i=document()->begin(); !i.contains(pos) && i!=document()->end(); i=i.next())
213
void ScriptEdit::handleContentsChange(int position, int, int)
215
if (d_changing_fmt) return; // otherwise we overwrite our own changes
216
QTextCursor cursor = textCursor();
217
cursor.setPosition(position);
218
cursor.mergeBlockFormat(d_fmt_default);
221
void ScriptEdit::execute()
223
QString fname = "<%1:%2>";
224
fname = fname.arg(name());
225
QTextCursor codeCursor = textCursor();
226
if (codeCursor.selectedText().isEmpty()){
227
codeCursor.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
228
codeCursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
230
fname = fname.arg(lineNumber(codeCursor.selectionStart()));
232
myScript->setName(fname);
233
myScript->setCode(codeCursor.selectedText().replace(QChar::ParagraphSeparator,"\n"));
234
printCursor.setPosition(codeCursor.selectionEnd(), QTextCursor::MoveAnchor);
235
printCursor.movePosition(QTextCursor::EndOfLine, QTextCursor::MoveAnchor);
237
d_changing_fmt = true;
239
codeCursor.mergeBlockFormat(d_fmt_failure);
241
codeCursor.mergeBlockFormat(d_fmt_success);
242
d_changing_fmt = false;
246
void ScriptEdit::executeAll()
248
QString fname = "<%1>";
249
fname = fname.arg(name());
250
myScript->setName(fname);
251
myScript->setCode(text());
252
printCursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
256
void ScriptEdit::evaluate()
258
QString fname = "<%1:%2>";
259
fname = fname.arg(name());
260
QTextCursor codeCursor = textCursor();
261
if (codeCursor.selectedText().isEmpty()){
262
codeCursor.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
263
codeCursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
265
fname = fname.arg(lineNumber(codeCursor.selectionStart()));
267
myScript->setName(fname);
268
myScript->setCode(codeCursor.selectedText().replace(QChar::ParagraphSeparator,"\n"));
269
printCursor.setPosition(codeCursor.selectionEnd(), QTextCursor::MoveAnchor);
270
printCursor.movePosition(QTextCursor::EndOfLine, QTextCursor::MoveAnchor);
271
QVariant res = myScript->eval();
273
d_changing_fmt = true;
275
codeCursor.mergeBlockFormat(d_fmt_failure);
277
codeCursor.mergeBlockFormat(d_fmt_success);
280
if (!res.isNull() && res.canConvert(QVariant::String)){
281
QString strVal = res.toString();
282
strVal.replace("\n", "\n#> ");
283
printCursor.insertText("\n");
284
printCursor.mergeBlockFormat(d_fmt_default);
285
if (!strVal.isEmpty())
286
printCursor.insertText("#> "+strVal+"\n");
289
d_changing_fmt = false;
291
setTextCursor(printCursor);
294
void ScriptEdit::exportPDF(const QString& fileName)
296
QTextDocument *doc = document();
298
printer.setColorMode(QPrinter::GrayScale);
299
printer.setCreator("SciDAVis");
300
printer.setOutputFormat(QPrinter::PdfFormat);
301
printer.setOutputFileName(fileName);
302
doc->print(&printer);
305
void ScriptEdit::print()
307
QTextDocument *doc = document();
309
printer.setColorMode(QPrinter::GrayScale);
310
QPrintDialog printDialog(&printer);
311
// TODO: Write a dialog to use more features of Qt4's QPrinter class
312
if (printDialog.exec() == QDialog::Accepted)
313
doc->print(&printer);
316
QString ScriptEdit::importASCII(const QString &filename)
318
QString filter = tr("Text") + " (*.txt *.TXT);;";
319
filter += scriptEnv->fileFilter();
320
filter += tr("All Files")+" (*)";
323
if (filename.isEmpty())
324
f = QFileDialog::getOpenFileName(name(), filter, this, 0, tr("Import Text From File"));
327
if (f.isEmpty()) return QString::null;
329
if (!file.open(IO_ReadOnly)){
330
QMessageBox::critical(this, tr("Error Opening File"), tr("Could not open file \"%1\" for reading.").arg(f));
331
return QString::null;
333
QTextStream s(&file);
334
s.setEncoding(QTextStream::UnicodeUTF8);
336
insert(s.readLine()+"\n");
341
QString ScriptEdit::exportASCII(const QString &filename)
343
QString filter = tr("Text") + " (*.txt *.TXT);;";
344
filter += scriptEnv->fileFilter();
345
filter += tr("All Files")+" (*)";
347
QString selectedFilter;
349
if (filename.isEmpty())
350
fn = QFileDialog::getSaveFileName(name(), filter, this, 0,
351
tr("Save Text to File"), &selectedFilter, false);
355
if ( !fn.isEmpty() ){
357
QString baseName = fi.fileName();
358
if (!baseName.contains(".")){
359
if (selectedFilter.contains(".txt"))
361
else if (selectedFilter.contains(".py"))
366
if (!f.open(IO_WriteOnly)){
367
QMessageBox::critical(0, tr("File Save Error"),
368
tr("Could not write to file: <br><h4> %1 </h4><p>Please verify that you have the right to write to this location!").arg(fn));
369
return QString::null;
373
t.setEncoding(QTextStream::UnicodeUTF8);
380
void ScriptEdit::updateIndentation()
382
QTextCursor cursor = textCursor();
383
QTextBlock para = cursor.block();
384
QString prev = para.previous().text();
386
for (i=0; prev[i].isSpace(); i++);
387
QString indent = prev.mid(0, i);
388
cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
389
cursor.insertText(indent);