~ubuntu-branches/ubuntu/maverick/scidavis/maverick

« back to all changes in this revision

Viewing changes to scidavis/src/ScriptEdit.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Ruben Molina
  • Date: 2009-09-06 11:34:04 UTC
  • Revision ID: james.westby@ubuntu.com-20090906113404-4awaey82l3686w4q
Tags: upstream-0.2.3
ImportĀ upstreamĀ versionĀ 0.2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
    File                 : ScriptEdit.cpp
 
3
    Project              : SciDAVis
 
4
    --------------------------------------------------------------------
 
5
    Copyright            : (C) 2006 by Ion Vasilief,
 
6
                           Tilman Benkert,
 
7
                           Knut Franke
 
8
    Email (use @ for *)  : ion_vasilief*yahoo.fr, thzs*gmx.net,
 
9
                           knut.franke*gmx.de
 
10
    Description          : Editor widget for scripting code
 
11
 
 
12
 ***************************************************************************/
 
13
 
 
14
/***************************************************************************
 
15
 *                                                                         *
 
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.                                    *
 
20
 *                                                                         *
 
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.                           *
 
25
 *                                                                         *
 
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                                           *
 
30
 *                                                                         *
 
31
 ***************************************************************************/
 
32
#include "ScriptEdit.h"
 
33
#include "Note.h"
 
34
 
 
35
#include <QAction>
 
36
#include <QMenu>
 
37
#include <QPrintDialog>
 
38
#include <QPrinter>
 
39
#include <QMessageBox>
 
40
#include <QFileDialog>
 
41
#include <QTextStream>
 
42
 
 
43
ScriptEdit::ScriptEdit(ScriptingEnv *env, QWidget *parent, const char *name)
 
44
  : QTextEdit(parent, name), scripted(env), d_error(false)
 
45
{
 
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&)));
 
49
 
 
50
        setLineWrapMode(NoWrap);
 
51
        setTextFormat(Qt::PlainText);
 
52
        setAcceptRichText (false);
 
53
        setFamily("Monospace");
 
54
 
 
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)));
 
58
 
 
59
        printCursor = textCursor();
 
60
 
 
61
        actionExecute = new QAction(tr("E&xecute"), this);
 
62
        actionExecute->setShortcut( tr("Ctrl+J") );
 
63
        connect(actionExecute, SIGNAL(activated()), this, SLOT(execute()));
 
64
 
 
65
        actionExecuteAll = new QAction(tr("Execute &All"), this);
 
66
        actionExecuteAll->setShortcut( tr("Ctrl+Shift+J") );
 
67
        connect(actionExecuteAll, SIGNAL(activated()), this, SLOT(executeAll()));
 
68
 
 
69
        actionEval = new QAction(tr("&Evaluate Expression"), this);
 
70
        actionEval->setShortcut( tr("Ctrl+Return") );
 
71
        connect(actionEval, SIGNAL(activated()), this, SLOT(evaluate()));
 
72
 
 
73
        actionPrint = new QAction(tr("&Print"), this);
 
74
        connect(actionPrint, SIGNAL(activated()), this, SLOT(print()));
 
75
 
 
76
        actionImport = new QAction(tr("&Import"), this);
 
77
        connect(actionImport, SIGNAL(activated()), this, SLOT(importASCII()));
 
78
 
 
79
        actionExport = new QAction(tr("&Export"), this);
 
80
        connect(actionExport, SIGNAL(activated()), this, SLOT(exportASCII()));
 
81
 
 
82
        functionsMenu = new QMenu(this);
 
83
        Q_CHECK_PTR(functionsMenu);
 
84
        connect(functionsMenu, SIGNAL(triggered(QAction *)), this, SLOT(insertFunction(QAction *)));
 
85
 
 
86
        connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(handleContentsChange(int,int,int)));
 
87
}
 
88
 
 
89
void ScriptEdit::customEvent(QEvent *e)
 
90
{
 
91
        if (e->type() == SCRIPTING_CHANGE_EVENT)
 
92
        {
 
93
                scriptingChangeEvent((ScriptingChangeEvent*)e);
 
94
                delete myScript;
 
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&)));
 
98
        }
 
99
}
 
100
 
 
101
void ScriptEdit::keyPressEvent(QKeyEvent *e)
 
102
{
 
103
        QTextEdit::keyPressEvent(e);
 
104
        if (e->key() == Qt::Key_Return)
 
105
                updateIndentation();
 
106
}
 
107
 
 
108
void ScriptEdit::contextMenuEvent(QContextMenuEvent *e)
 
109
{
 
110
        QMenu *menu = createStandardContextMenu();
 
111
        Q_CHECK_PTR(menu);
 
112
 
 
113
        menu->addAction(actionPrint);
 
114
        menu->addAction(actionImport);
 
115
        menu->addAction(actionExport);
 
116
        menu->addSeparator();
 
117
 
 
118
        menu->addAction(actionExecute);
 
119
        menu->addAction(actionExecuteAll);
 
120
        menu->addAction(actionEval);
 
121
 
 
122
        if (parent()->inherits("Note"))
 
123
        {
 
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);
 
130
        }
 
131
 
 
132
        functionsMenu->clear();
 
133
        functionsMenu->setTearOffEnabled(true);
 
134
        QStringList flist = scriptEnv->mathFunctions();
 
135
        QMenu *submenu=NULL;
 
136
        for (int i=0; i<flist.size(); i++)
 
137
        {
 
138
                QAction *newAction;
 
139
                QString menupart;
 
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];
 
143
                else
 
144
                        menupart = "";*/
 
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]))
 
147
                        menupart = "";
 
148
                else
 
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]);
 
154
                } else
 
155
                        newAction = functionsMenu->addAction(flist[i]);
 
156
                newAction->setData(i);
 
157
                newAction->setWhatsThis(scriptEnv->mathFunctionDoc(flist[i]));
 
158
        }
 
159
        functionsMenu->setTitle(tr("&Functions"));
 
160
        menu->addMenu(functionsMenu);
 
161
 
 
162
        menu->exec(e->globalPos());
 
163
        delete menu;
 
164
}
 
165
 
 
166
void ScriptEdit::insertErrorMsg(const QString &message)
 
167
{
 
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);
 
174
        d_error = true;
 
175
}
 
176
 
 
177
void ScriptEdit::scriptPrint(const QString &text)
 
178
{
 
179
        if(lineNumber(printCursor.position()) == lineNumber(textCursor().selectionEnd()))
 
180
                printCursor.insertText("\n");
 
181
        printCursor.insertText(text);
 
182
}
 
183
 
 
184
void ScriptEdit::insertFunction(const QString &fname)
 
185
{
 
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);
 
197
        }
 
198
}
 
199
 
 
200
void ScriptEdit::insertFunction(QAction *action)
 
201
{
 
202
        insertFunction(scriptEnv->mathFunctions()[action->data().toInt()]);
 
203
}
 
204
 
 
205
int ScriptEdit::lineNumber(int pos) const
 
206
{
 
207
        int n=1;
 
208
        for(QTextBlock i=document()->begin(); !i.contains(pos) && i!=document()->end(); i=i.next())
 
209
                n++;
 
210
        return n;
 
211
}
 
212
 
 
213
void ScriptEdit::handleContentsChange(int position, int, int)
 
214
{
 
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);
 
219
}
 
220
 
 
221
void ScriptEdit::execute()
 
222
{
 
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);
 
229
        }
 
230
        fname = fname.arg(lineNumber(codeCursor.selectionStart()));
 
231
 
 
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);
 
236
        myScript->exec();
 
237
        d_changing_fmt = true;
 
238
        if (d_error)
 
239
                codeCursor.mergeBlockFormat(d_fmt_failure);
 
240
        else
 
241
                codeCursor.mergeBlockFormat(d_fmt_success);
 
242
        d_changing_fmt = false;
 
243
        d_error = false;
 
244
}
 
245
 
 
246
void ScriptEdit::executeAll()
 
247
{
 
248
        QString fname = "<%1>";
 
249
        fname = fname.arg(name());
 
250
        myScript->setName(fname);
 
251
        myScript->setCode(text());
 
252
        printCursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
 
253
        myScript->exec();
 
254
}
 
255
 
 
256
void ScriptEdit::evaluate()
 
257
{
 
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);
 
264
        }
 
265
        fname = fname.arg(lineNumber(codeCursor.selectionStart()));
 
266
 
 
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();
 
272
 
 
273
        d_changing_fmt = true;
 
274
        if (d_error)
 
275
                codeCursor.mergeBlockFormat(d_fmt_failure);
 
276
        else
 
277
                codeCursor.mergeBlockFormat(d_fmt_success);
 
278
 
 
279
        if (res.isValid())
 
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");
 
287
                }
 
288
 
 
289
        d_changing_fmt = false;
 
290
        d_error = false;
 
291
        setTextCursor(printCursor);
 
292
}
 
293
 
 
294
void ScriptEdit::exportPDF(const QString& fileName)
 
295
{
 
296
        QTextDocument *doc = document();
 
297
        QPrinter printer;
 
298
        printer.setColorMode(QPrinter::GrayScale);
 
299
        printer.setCreator("SciDAVis");
 
300
    printer.setOutputFormat(QPrinter::PdfFormat);
 
301
    printer.setOutputFileName(fileName);
 
302
        doc->print(&printer);
 
303
}
 
304
 
 
305
void ScriptEdit::print()
 
306
{
 
307
        QTextDocument *doc = document();
 
308
        QPrinter printer;
 
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);
 
314
}
 
315
 
 
316
QString ScriptEdit::importASCII(const QString &filename)
 
317
{
 
318
        QString filter = tr("Text") + " (*.txt *.TXT);;";
 
319
        filter += scriptEnv->fileFilter();
 
320
        filter += tr("All Files")+" (*)";
 
321
 
 
322
        QString f;
 
323
        if (filename.isEmpty())
 
324
                f = QFileDialog::getOpenFileName(name(),  filter, this, 0, tr("Import Text From File"));
 
325
        else
 
326
                f = filename;
 
327
        if (f.isEmpty()) return QString::null;
 
328
        QFile file(f);
 
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;
 
332
        }
 
333
        QTextStream s(&file);
 
334
        s.setEncoding(QTextStream::UnicodeUTF8);
 
335
        while (!s.atEnd())
 
336
                insert(s.readLine()+"\n");
 
337
        file.close();
 
338
        return f;
 
339
}
 
340
 
 
341
QString ScriptEdit::exportASCII(const QString &filename)
 
342
{
 
343
        QString filter = tr("Text") + " (*.txt *.TXT);;";
 
344
        filter += scriptEnv->fileFilter();
 
345
        filter += tr("All Files")+" (*)";
 
346
 
 
347
        QString selectedFilter;
 
348
        QString fn;
 
349
        if (filename.isEmpty())
 
350
                fn = QFileDialog::getSaveFileName(name(), filter, this, 0,
 
351
                                tr("Save Text to File"), &selectedFilter, false);
 
352
        else
 
353
                fn = filename;
 
354
                
 
355
        if ( !fn.isEmpty() ){
 
356
                QFileInfo fi(fn);
 
357
                QString baseName = fi.fileName();
 
358
                if (!baseName.contains(".")){
 
359
                        if (selectedFilter.contains(".txt"))
 
360
                                fn.append(".txt");
 
361
                        else if (selectedFilter.contains(".py"))
 
362
                                fn.append(".py");
 
363
                }
 
364
 
 
365
                QFile f(fn);
 
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;
 
370
                }
 
371
                        
 
372
                QTextStream t( &f );
 
373
                t.setEncoding(QTextStream::UnicodeUTF8);
 
374
                t << text();
 
375
                f.close();
 
376
        }
 
377
        return fn;
 
378
}
 
379
 
 
380
void ScriptEdit::updateIndentation()
 
381
{
 
382
        QTextCursor cursor = textCursor();
 
383
        QTextBlock para = cursor.block();
 
384
        QString prev = para.previous().text();
 
385
        int i;
 
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);
 
390
}