1 //
2 // This file is part of the source of CoCoALib, the CoCoA Library.
3 //
4 // CoCoALib is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // CoCoALib is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with CoCoALib. If not, see <http://www.gnu.org/licenses/>.
16
17 #ifdef C5IDE
18
19 #include <QFileDialog>
20 #include <QFileInfo>
21 #include <QList>
22 #include <QPlastiqueStyle>
23 #include <QFontDialog>
24 #include <QCoreApplication>
25 #include <QSettings>
26
27 #include "Banner.H"
28 #include "C5.H"
29 #include "C5Utils.H"
30 #include "CoCoA/GlobalManager.H"
31 #include "CoCoA/interrupt.H"
32 #include "qce-config.h"
33 #include "qeditor.h"
34 #include "qlanguagefactory.h"
35 #include "qlinemarksinfocenter.h"
36 #include "qeditorinputbinding.h"
37 #include "widgets/qgotolinepanel.h"
38 #include "widgets/qfoldpanel.h"
39 #include "widgets/qstatuspanel.h"
40 #include "widgets/qlinemarkpanel.h"
41 #include "widgets/qsearchreplacepanel.h"
42 #include "widgets/qlinenumberpanel.h"
43 #include "widgets/qlinechangepanel.h"
44
45 using namespace std;
46 using namespace boost;
47 using namespace CoCoA;
48 using namespace CoCoA::LexerNS;
49 using namespace CoCoA::AST;
50 using namespace CoCoA::InterpreterNS;
51
initQCodeEdit()52 void initQCodeEdit() {
53 // the following are needed to statically (and successfully ;-) ) link QCodeEdit
54 // note: this function must be outside any namespace (because of the macro Q_INIT_RESOURCE)
55 QGotoLinePanel::_register();
56 QFoldPanel::_register();
57 QStatusPanel::_register();
58 QLineMarkPanel::_register();
59 QSearchReplacePanel::_register();
60 QLineNumberPanel::_register();
61 QLineChangePanel::_register();
62 Q_INIT_RESOURCE(Edyuk);
63 }
64
65 namespace {
66 const QString SETTING_FONT_FAMILY("font/family");
67 const QString SETTING_FONT_POINTSIZE("font/pointSize");
68 const QString SETTING_FONT_WEIGHT("font/weight");
69 const QString SETTING_FONT_ITALIC("font/italic");
70 const QString SETTING_COLORSCHEME_NAME("colorScheme/name");
71 const QString SETTING_EMACS_LIKE_KEYBINDINGS("emacs/keyBindings");
72 }
73
74 namespace CoCoA {
75
76 namespace AST {
eval(RuntimeEnvironment * re)77 intrusive_ptr<Value> Expression::eval(RuntimeEnvironment *re) const {
78 Interpreter * const interpreter = re->interpreter;
79 bool doStepOver = false;
80 if (interpreter->singleStepExecution && !CoCoA::GetAndResetSignalReceived() && !this->skipDebugging())
81 doStepOver = interpreter->pause(this);
82 intrusive_ptr<Value> result;
83 try {
84 result = this->implEval(re);
85 if (doStepOver && interpreter->doStepOver)
86 interpreter->singleStepExecution = true;
87 } catch (InterruptException &) {
88 throw;
89 } catch (RuntimeException &) {
90 if (doStepOver && interpreter->doStepOver)
91 interpreter->singleStepExecution = true;
92 throw;
93 }
94 return result;
95 }
96
skipDebugging()97 bool ParsedObject::skipDebugging() const {
98 return false;
99 }
100
skipDebugging()101 bool EvalStatement::skipDebugging() const {
102 return true;
103 }
104
skipDebugging()105 bool Statements::skipDebugging() const {
106 return true;
107 }
108
skipDebugging()109 bool EmptyStatement::skipDebugging() const {
110 return true;
111 }
112
skipDebugging()113 bool InvocationExpression::skipDebugging() const {
114 return this->targetExp->skipDebugging();
115 }
116
execute(RuntimeEnvironment * re)117 void Statement::execute(RuntimeEnvironment *re) {
118 Interpreter * const interpreter = re->interpreter;
119 bool doStepOver = false;
120 if (interpreter->singleStepExecution && !CoCoA::GetAndResetSignalReceived() && !this->skipDebugging()) {
121 doStepOver = interpreter->pause(this);
122 if (doStepOver) {
123 if (dynamic_cast<BreakStatement *>(this) || dynamic_cast<ContinueStatement *>(this))
124 re->getCurrentFrame()->singleStepOnPop = true;
125 if (dynamic_cast<ReturnStatement *>(this))
126 re->getCurrentFunctionFrame()->singleStepOnPop = true;
127 }
128 }
129 try {
130 this->implExecute(re);
131 if (doStepOver && interpreter->doStepOver)
132 interpreter->singleStepExecution = true;
133 } catch (InterruptException &) {
134 throw;
135 } catch (RuntimeException &) {
136 if (doStepOver && interpreter->doStepOver)
137 interpreter->singleStepExecution = true;
138 throw;
139 }
140 }
141 } // namespace AST
142
143 namespace {
144 const int COL_NAME = 0;
145 const int COL_KIND = 1;
146 const int COL_VALUE = 2;
147 }
148
149 namespace InterpreterNS {
pause(intrusive_ptr<const ParsedObject> po)150 bool Interpreter::pause(intrusive_ptr<const ParsedObject> po) {
151 assert(this_thread::get_id()!=IDE::Console::guiThreadId);
152 const Frame *tl = this->runtimeEnvironment.getTopLevelFrame();
153 for(Frame *f=this->runtimeEnvironment.getCurrentFrame(); f>=tl;)
154 (f--)->singleStepOnPop = false;
155 this->doStepOver = false;
156 this->pausedOn = po;
157 InterpreterStatus status = this->status;
158 assert(status==IS_RUNNING || status==IS_RUNNING_BUILTIN);
159 this->status = IS_PAUSED;
160 unique_lock<mutex> lock(this->mut);
161 this->mustResume = false;
162 do
163 this->condVar.wait(lock);
164 while (!this->mustResume);
165 this->pausedOn = 0;
166 this->status = status;
167 this->runtimeEnvironment.getOutputStream()->flush(); // this is used to sync (that is, wait until the GUI notices the status has changed)
168 this->checkForInterrupts(po);
169 return this->doStepOver;
170 }
171
resume()172 void Interpreter::resume() {
173 assert(this_thread::get_id()==IDE::Console::guiThreadId);
174 assert(this->status==IS_PAUSED);
175 this->mustResume = true;
176 this->condVar.notify_one();
177 }
178
initTreeWidget(QTreeWidgetItem * item)179 void RightValue::initTreeWidget(QTreeWidgetItem *item) const {
180 ostringstream os;
181 os << this;
182 item->setText(COL_VALUE, QString::fromStdString(os.str()));
183 }
184
initTreeWidget(QTreeWidgetItem * item)185 void VoidValue::initTreeWidget(QTreeWidgetItem *item) const {
186 item->setText(COL_VALUE, "Uninitialized");
187 }
188
initTreeWidget(QTreeWidgetItem * item)189 void BuiltInFunction::initTreeWidget(QTreeWidgetItem *item) const {
190 item->setText(COL_VALUE, "A built-in fn-proc");
191 }
192
initTreeWidget(QTreeWidgetItem * item)193 void UserDefinedFunction::initTreeWidget(QTreeWidgetItem *item) const {
194 item->setText(COL_VALUE, "A user-defined fn-proc");
195 }
196
initTreeWidget(QTreeWidgetItem * item)197 void TYPE::initTreeWidget(QTreeWidgetItem *item) const {
198 item->setText(COL_VALUE, "A type");
199 }
200
initTreeWidget(QTreeWidgetItem * item)201 void OSTREAM::initTreeWidget(QTreeWidgetItem *item) const {
202 item->setText(COL_VALUE, "An output-stream");
203 }
204
initTreeWidget(QTreeWidgetItem * item)205 void LIST::initTreeWidget(QTreeWidgetItem *item) const {
206 const ContainerType::size_type size = this->size();
207 item->setText(COL_VALUE, QString::fromStdString("A "+lexical_cast<string>(size)+"-element list"));
208 for(ContainerType::size_type a=0; a<size; ++a) {
209 QTreeWidgetItem * const child = new QTreeWidgetItem();
210 child->setText(COL_NAME, QString::fromStdString("["+lexical_cast<string>(a+1)+"]"));
211 item->addChild(child);
212 container[a]->initTreeWidget(child);
213 }
214 }
215
initTreeWidget(QTreeWidgetItem * item)216 void TaggedValue::initTreeWidget(QTreeWidgetItem *item) const {
217 item->setText(COL_VALUE, QString::fromStdString("A tagged value"));
218 QTreeWidgetItem * const child1 = new QTreeWidgetItem();
219 child1->setText(COL_NAME, QString::fromStdString("Tag"));
220 child1->setText(COL_VALUE, QString::fromStdString(this->tag));
221 item->addChild(child1);
222 QTreeWidgetItem * const child2 = new QTreeWidgetItem();
223 child2->setText(COL_NAME, QString::fromStdString("Value"));
224 item->addChild(child2);
225 this->value->initTreeWidget(child2);
226 }
227
initTreeWidget(QTreeWidgetItem * item)228 void RECORD::initTreeWidget(QTreeWidgetItem *item) const {
229 const MapType::size_type size = this->numberOfFields();
230 item->setText(COL_VALUE, QString::fromStdString("A "+lexical_cast<string>(size)+"-field record"));
231 for(MapType::const_iterator pos = this->fields.begin(); pos!=this->fields.end(); ++pos) {
232 QTreeWidgetItem * const child = new QTreeWidgetItem();
233 child->setText(COL_NAME, QString::fromStdString("."+pos->first));
234 item->addChild(child);
235 pos->second->initTreeWidget(child);
236 }
237 }
238
initTreeWidget(QTreeWidgetItem * item)239 void IntMapValue::initTreeWidget(QTreeWidgetItem *item) const {
240 item->setText(COL_VALUE, QString::fromStdString("A map"));
241 for(MapType::const_iterator pos = this->map.begin(); pos!=this->map.end(); ++pos) {
242 QTreeWidgetItem * const child = new QTreeWidgetItem();
243 child->setText(COL_NAME, QString::fromStdString("["+lexical_cast<string>(pos->first)+"]"));
244 item->addChild(child);
245 pos->second->initTreeWidget(child);
246 }
247 }
248 } // namespace InterpreterNS
249
250 namespace IDE {
251
252 thread::id Console::guiThreadId;
253
highlightBlock(const QString & text)254 void CocoaHighlighter::highlightBlock(const QString &text) {
255 assert(this_thread::get_id()==Console::guiThreadId);
256 if (!this->alwaysEnabled) {
257 QTextBlockUserData *data = this->currentBlockUserData();
258 if (data) {
259 assert(dynamic_cast<HighlightingInfo *>(data));
260 if (!static_cast<HighlightingInfo *>(data)->doHighlight)
261 return;
262 } else {
263 this->setCurrentBlockUserData(new HighlightingInfo(this->enabled));
264 if (!this->enabled)
265 return;
266 }
267 }
268 this->unclosedComment = this->unclosedStringLiteral = false;
269 const int textLenght = text.length();
270 setCurrentBlockState(0);
271 int index=0;
272 const int previous = previousBlockState();
273 if (previous==IN_SINGLEQUOTE_STRING) {
274 index = -1;
275 goto findSingleQuoteClosing;
276 }
277 if (previous==IN_DOUBLEQUOTE_STRING) {
278 index = -1;
279 goto findDoubleQuoteClosing;
280 }
281 if (previous==IN_TRIPLEQUOTE_STRING) {
282 index = -3;
283 goto findTripleQuoteClosing;
284 }
285 if (previous==IN_COMMENT) {
286 index = -2;
287 goto findCommentClosing;
288 }
289 while(index<textLenght) {
290 if (text.mid(index,2)==tr("--") || text.mid(index,2)==tr("//")) {
291 int endPos = text.indexOf(this->singlelineCommentEndRE, index+2);
292 int len;
293 if (endPos==-1) {
294 if (index<=0)
295 index = 0;
296 len = textLenght-index;
297 } else {
298 len = this->singlelineCommentEndRE.matchedLength();
299 if (index>=0)
300 len += 2;
301 else
302 index = 0;
303 }
304 setFormat(index, len, this->commentFormat);
305 index += len;
306 continue;
307 }
308 if (text.mid(index,2)==tr("/*")) {
309 findCommentClosing:
310 setCurrentBlockState(IN_COMMENT);
311 int endPos = text.indexOf(this->commentEndRE, index+2);
312 int len;
313 if (endPos==-1) {
314 this->unclosedComment = true;
315 if (index<=0)
316 index = 0;
317 len = textLenght-index;
318 } else {
319 len = this->commentEndRE.matchedLength();
320 if (index>=0)
321 len += 2;
322 else
323 index = 0;
324 setCurrentBlockState(NONE);
325 }
326 setFormat(index, len, this->commentFormat);
327 index += len;
328 continue;
329 }
330 if (text.mid(index, 3)==tr("\"\"\"")) {
331 findTripleQuoteClosing:
332 setCurrentBlockState(IN_TRIPLEQUOTE_STRING);
333 int endPos = text.indexOf(this->tripleQuoteliteralStringEndRE, index+3);
334 int len;
335 if (endPos==-1) {
336 this->unclosedStringLiteral = true;
337 if (index<=0)
338 index = 0;
339 len = textLenght-index;
340 } else {
341 len = this->tripleQuoteliteralStringEndRE.matchedLength();
342 if (index>=0)
343 len += 3;
344 else
345 index = 0;
346 setCurrentBlockState(NONE);
347 }
348 setFormat(index, len, this->literalStringFormat);
349 index += len;
350 continue;
351 }
352 if (text.mid(index, 1)==tr("\'")) {
353 findSingleQuoteClosing:
354 setCurrentBlockState(IN_SINGLEQUOTE_STRING);
355 int endPos = text.indexOf(this->singleQuoteliteralStringEndRE, index+1);
356 int len;
357 if (endPos==-1) {
358 this->unclosedStringLiteral = true;
359 if (index<=0)
360 index = 0;
361 len = textLenght-index;
362 } else {
363 len = this->singleQuoteliteralStringEndRE.matchedLength();
364 if (index>=0)
365 ++len;
366 else
367 index = 0;
368 setCurrentBlockState(NONE);
369 }
370 setFormat(index, len, this->literalStringFormat);
371 index += len;
372 continue;
373 }
374 if (text.mid(index, 1)==tr("\"")) {
375 findDoubleQuoteClosing:
376 setCurrentBlockState(IN_DOUBLEQUOTE_STRING);
377 int endPos = text.indexOf(this->doubleQuoteliteralStringEndRE, index+1);
378 int len;
379 if (endPos==-1) {
380 this->unclosedStringLiteral = true;
381 if (index<=0)
382 index = 0;
383 len = textLenght-index;
384 } else {
385 len = this->doubleQuoteliteralStringEndRE.matchedLength();
386 if (index>=0)
387 ++len;
388 else
389 index = 0;
390 setCurrentBlockState(NONE);
391 }
392 setFormat(index, len, this->literalStringFormat);
393 index += len;
394 continue;
395 }
396 const char firstChar = text.mid(index, 1).toStdString()[0];
397 if (IsBlank(firstChar) && text.indexOf(this->blanksRE, index)==index) {
398 index += this->blanksRE.matchedLength();
399 continue;
400 }
401 if (firstChar=='_' || firstChar=='$')
402 goto try_identifier;
403 if (firstChar=='?')
404 goto try_keyword;
405 if (isalpha(firstChar)) {
406 if (text.indexOf(this->typeRE, index)==index) {
407 const int len = this->typeRE.matchedLength();
408 setFormat(index, len, this->typeFormat);
409 index += len;
410 continue;
411 }
412 if (text.indexOf(this->constantRE, index)==index) {
413 const int len = this->constantRE.matchedLength();
414 setFormat(index, len, this->constantFormat);
415 index += len;
416 continue;
417 }
418 try_keyword: if (text.indexOf(this->keywordRE, index)==index) { // note: RECORD is not a keyword (while all other casings of "record", are)
419 const int len = this->keywordRE.matchedLength();
420 setFormat(index, len, this->keywordFormat);
421 index += len;
422 continue;
423 }
424 try_identifier: if (text.indexOf(this->identifierRE, index)==index) {
425 const int len = this->identifierRE.matchedLength();
426 setFormat(index, len, this->identifierFormat);
427 index += len;
428 continue;
429 }
430 }
431 if (isdigit(firstChar) && text.indexOf(this->numericLiteralRE, index)==index) {
432 const int len = this->numericLiteralRE.matchedLength();
433 setFormat(index, len, this->numericLiteralFormat);
434 index += len;
435 continue;
436 }
437 if (text.indexOf(this->operatorRE, index)==index) {
438 int len = this->operatorRE.matchedLength();
439 if (text.indexOf(this->parenthesisRE, index)==index) {
440 len = this->parenthesisRE.matchedLength();
441 setFormat(index, len, this->operatorFormat);
442 }
443 index += len;
444 continue;
445 }
446 if (this->highlightUnknownChars)
447 setFormat(index, 1, this->unknownFormat);
448 ++index;
449 }
450 }
451
CocoaHighlighter(QTextDocument * parent,bool highlightUnknownChars,bool alwaysEnabled)452 CocoaHighlighter::CocoaHighlighter(QTextDocument *parent, bool highlightUnknownChars, bool alwaysEnabled) :
453 QSyntaxHighlighter(parent),
454 highlightUnknownChars(highlightUnknownChars),
455 blanksRE("\\s+", Qt::CaseInsensitive, QRegExp::RegExp2),
456 keywordRE(
457 "[\\?]|\\b(?:Alias|And|Block|Break|Catch|Ciao|Clear|Continue|Define|"
458 "DegLex|DegRevLex|Delete|Describe|Destroy|Do|Elif|Elim|"
459 "Else|End|EndAlias|EndBlock|EndCatch|EndDefine|EndForeach|"
460 "EndFor|EndFunc|EndIf|EndPackage|EndRepeat|EndTry|"
461 "EndUsing|EndWhile|Exit|Export|False|Foreach|For|Func|If|"
462 "ImportByRef|ImportByValue|In|IsDefined|IsIn|Lex|On|Opt|"
463 "Ord|Or|Package|PosTo|PrintLn|Print|Protect|Quit|Record|"
464 "Ref|Repeat|Return|Set|Skip|Source|Step|Then|Time|To|"
465 "TopLevel|ToPos|True|Try|Unset|UponError|Until|Unprotect|"
466 "Use|Using|Var|Weights|While|Xel)\\b", Qt::CaseInsensitive, QRegExp::RegExp2),
467 constantRE("\\b(?:True|False|Lex|Xel|DegLex|DegRevLex|ToPos|PosTo)\\b", Qt::CaseInsensitive, QRegExp::RegExp2),
468 singleQuoteliteralStringEndRE("(?:[^\"\\\\]|(?:\\\\(?:[nrta'\"\\\\]|(?:x[0-9a-fA-F]{2}))))*\'", Qt::CaseSensitive, QRegExp::RegExp2),
469 doubleQuoteliteralStringEndRE("(?:[^\"\\\\]|(?:\\\\(?:[nrta'\"\\\\]|(?:x[0-9a-fA-F]{2}))))*\"", Qt::CaseSensitive, QRegExp::RegExp2),
470 tripleQuoteliteralStringEndRE("(?:[^\"\\\\]|(?:\\\\(?:[nrta'\"\\\\]|(?:x[0-9a-fA-F]{2}))))*\"\"\"", Qt::CaseSensitive, QRegExp::RegExp2),
471 identifierRE("[a-z$_][a-z0-9_]*", Qt::CaseInsensitive, QRegExp::RegExp2),
472 commentEndRE("(?:[^\\*]|(?:\\*[^/]))*\\*/", Qt::CaseInsensitive, QRegExp::RegExp2),
473 singlelineCommentEndRE("[^\\n]*", Qt::CaseInsensitive, QRegExp::RegExp2),
474 numericLiteralRE("\\d+(?:\\.\\d+)?", Qt::CaseInsensitive, QRegExp::RegExp2),
475 typeRE("\\b(?:BOOL|FUNCTION|LIST|INT|RAT|RECORD|TYPE|STRING|VOID|ERROR|OSTREAM"
476 "RINGELEM|RATFUN|MODULEELEM|IDEAL|MODULE|MAT|RING|PACKAGE|RINGHOM|INTMAP)\\b", Qt::CaseSensitive, QRegExp::RegExp2),
477 parenthesisRE("[()\\[\\]]|\\$\\{|\\}\\$", Qt::CaseInsensitive, QRegExp::RegExp2), // parenthesisRE is a quick-hack because QCodeEdit cannot highlight operators correctly
478 operatorRE("[\\+\\-\\*/:<>=()\\[\\]|%^;,]|<=|>=|<>|:{1,2}=|<<|><|::|\\$\\{|\\}\\$|\\.{1,3}", Qt::CaseInsensitive, QRegExp::RegExp2),
479 unclosedComment(false),
480 unclosedStringLiteral(false),
481 enabled(true),
482 alwaysEnabled(alwaysEnabled)
483 {}
484
setFormats(const ColorScheme & scheme)485 void CocoaHighlighter::setFormats(const ColorScheme &scheme) {
486 this->literalStringFormat.setForeground(scheme.stringLiterals.foreground);
487 if (scheme.stringLiterals.background.isValid())
488 this->literalStringFormat.setBackground(scheme.stringLiterals.background);
489 this->constantFormat.setForeground(scheme.constants.foreground);
490 if (scheme.constants.background.isValid())
491 this->constantFormat.setBackground(scheme.constants.background);
492 this->commentFormat.setForeground(scheme.comments.foreground);
493 if (scheme.comments.background.isValid())
494 this->commentFormat.setBackground(scheme.comments.background);
495 this->keywordFormat.setForeground(scheme.keywords.foreground);
496 if (scheme.keywords.background.isValid())
497 this->keywordFormat.setBackground(scheme.keywords.background);
498 this->typeFormat.setForeground(scheme.types.foreground);
499 if (scheme.types.background.isValid())
500 this->typeFormat.setBackground(scheme.types.background);
501 this->numericLiteralFormat.setForeground(scheme.numericalLiterals.foreground);
502 if (scheme.numericalLiterals.background.isValid())
503 this->numericLiteralFormat.setBackground(scheme.numericalLiterals.background);
504 this->unknownFormat.setBackground(scheme.unknownChars.foreground);
505 if (scheme.unknownChars.background.isValid())
506 this->unknownFormat.setForeground(scheme.unknownChars.background);
507 }
508
IdeErrorReporter(LexerNS::WarningSeverity warningLevel,Console * console,const boost::intrusive_ptr<IdeOutputStream> outputStream)509 IdeErrorReporter::IdeErrorReporter(LexerNS::WarningSeverity warningLevel, Console *console, const boost::intrusive_ptr<IdeOutputStream> outputStream) :
510 ErrorReporter(warningLevel, outputStream),
511 console(console)
512 {
513 this->errorFormat.setFontWeight(QFont::Bold);
514 this->errorFormat.setForeground(Qt::red);
515 this->warningFormat.setFontWeight(QFont::Bold);
516 this->warningFormat.setForeground(Qt::blue);
517 this->calledByFormat.setFontWeight(QFont::Bold);
518 this->whereFormat.setFontWeight(QFont::Bold);
519 this->contextFormat.setFontWeight(QFont::Bold);
520 this->boldFormat.setFontWeight(QFont::Bold);
521 this->highlightErrorFormat.setUnderlineColor(Qt::red);
522 this->highlightErrorFormat.setFontUnderline(true);
523 this->highlightErrorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
524 }
525
closeEvent(QCloseEvent * event)526 void Console::closeEvent(QCloseEvent *event) {
527 event->ignore();
528 this->showMinimized();
529 }
530
closeEvent(QCloseEvent * event)531 void Debugger::closeEvent(QCloseEvent *event) {
532 event->ignore();
533 this->showMinimized();
534 }
535
IdeOutputStream(Console * console)536 IdeOutputStream::IdeOutputStream(Console *console) :
537 OSTREAM(false, false),
538 console(console)
539 {
540 boldFormat.setFontWeight(QFont::Bold);
541 }
542
execute(Console *)543 void FlushQC::execute(Console *) {
544 assert(this_thread::get_id()==Console::guiThreadId);
545 unique_lock<mutex> lock(this->mut);
546 this->flushed = true;
547 this->condVar.notify_one();
548 }
549
550 intrusive_ptr<FlushQC> FlushQC::theInstance(new FlushQC);
551
flush()552 void IdeOutputStream::flush() {
553 assert(this_thread::get_id()==this->console->interpreterThreadId);
554 intrusive_ptr<FlushQC> flushQC = FlushQC::theInstance;
555 unique_lock<mutex> lock(flushQC->mut);
556 flushQC->flushed = false;
557 this->console->postCommand(flushQC);
558 do
559 flushQC->condVar.wait(lock);
560 while (!flushQC->flushed);
561 }
562
print(const string & s,bool highlight,QTextCharFormat format)563 void IdeOutputStream::print(const string &s, bool highlight, QTextCharFormat format) {
564 this->console->postCommand(new PrintQC(s, highlight, format));
565 }
566
print(const string & s)567 intrusive_ptr<OSTREAM> IdeOutputStream::print(const string &s) {
568 this->console->postCommand(new PrintQC(s, false, normalFormat));
569 return this;
570 }
571
print(intrusive_ptr<const RightValue> v)572 intrusive_ptr<OSTREAM> IdeOutputStream::print(intrusive_ptr<const RightValue> v) {
573 ostringstream ss;
574 ss << v;
575 this->console->postCommand(new PrintQC(ss.str(), false, normalFormat));
576 return this;
577 }
578
579 intrusive_ptr<PrintQC> PrintQC::newline(new PrintQC("\n", false, IdeOutputStream::normalFormat));
580 intrusive_ptr<PrintQC> PrintQC::newlineHL(new PrintQC("\n", true, IdeOutputStream::normalFormat));
581
newline()582 intrusive_ptr<OSTREAM> IdeOutputStream::newline() {
583 this->console->postCommand(PrintQC::newline);
584 return this;
585 }
586
newlineHL()587 intrusive_ptr<OSTREAM> IdeOutputStream::newlineHL() {
588 this->console->postCommand(PrintQC::newlineHL);
589 return this;
590 }
591
execute(Console * console)592 void ClearReportedLocationsQC::execute(Console *console) {
593 console->clearReportedLocations();
594 }
595
596 intrusive_ptr<ClearReportedLocationsQC> ClearReportedLocationsQC::theInstance(new ClearReportedLocationsQC);
597
doReadNextLine(const LexerStatus & ls,const ParserNS::ParserStatus & ps,string & chars)598 bool IdeLineProvider::doReadNextLine(const LexerStatus &ls, const ParserNS::ParserStatus &ps, string &chars)
599 {
600 assert(this_thread::get_id()==this->console->interpreterThreadId);
601 this->console->outputStream->print(prompt(ls, ps), true, IdeOutputStream::boldFormat);
602 unique_lock<mutex> lock(this->mut);
603 for(;;)
604 {
605 const Interpreter::InterpreterStatus status = this->console->interpreter->getStatus();
606 assert(status==Interpreter::IS_WAITING_FOR_COMMAND || status==Interpreter::IS_WAITING_FOR_COMMAND_COMPLETION);
607 if (!this->enteredLines.empty())
608 {
609 chars = this->enteredLines.front();
610 this->enteredLines.pop_front();
611 // if (!IsWhiteSpace(chars)) ...
612 if (chars.find_first_not_of(" \t") != string::npos)
613 this->console->interpreter->UpdateStatusToWFCC();
614 break;
615 }
616 if (status==Interpreter::IS_WAITING_FOR_COMMAND && !this->enteredFullCommands.empty())
617 {
618 chars = this->enteredFullCommands.front();
619 this->enteredFullCommands.pop_front();
620 this->console->postCommand(ClearReportedLocationsQC::theInstance);
621 break;
622 }
623 this->condVar.wait(lock);
624 }
625 this->console->outputStream->print(chars, true, IdeOutputStream::normalFormat);
626 this->console->outputStream->newline();
627 chars += '\n';
628 return true;
629 }
630
enterLine(const string & line,bool isFullCommand)631 void IdeLineProvider::enterLine(const string &line, bool isFullCommand) {
632 assert(this_thread::get_id()==Console::guiThreadId);
633 unique_lock<mutex> lock(mut);
634 (isFullCommand ? this->enteredFullCommands : this->enteredLines).push_back(line);
635 this->condVar.notify_one();
636 }
637
implReportWarning(const string & msg)638 void IdeErrorReporter::implReportWarning(const string &msg) {
639 assert(this_thread::get_id()==this->console->interpreterThreadId);
640 this->printWarning();
641 this->outputStream->print(msg)->newline();
642 }
643
outputHighlightedChars(const CharPointer & from,const CharPointer & to)644 void IdeErrorReporter::outputHighlightedChars(const CharPointer &from, const CharPointer &to) {
645 assert(this_thread::get_id()==this->console->interpreterThreadId);
646 assert(dynamic_pointer_cast<IdeOutputStream>(this->outputStream));
647 IdeOutputStream * const ios = static_pointer_cast<IdeOutputStream>(this->outputStream).get();
648 if (!*from) {
649 ios->print("\n// <End of file>", true, IdeOutputStream::normalFormat);
650 ios->newline();
651 return;
652 }
653 intrusive_ptr<const Line> fromLine = from.getLine();
654 intrusive_ptr<const Line> toLine = to.getLine();
655 if (fromLine==toLine)
656 return outputHighlightedLine(fromLine, from.getPositionInLine(), to.getPositionInLine(), false);
657 if (!*to) {
658 ios->print("\n// ... <End of file>", true, IdeOutputStream::normalFormat);
659 this->outputStream->newline();
660 return;
661 }
662 this->outputHighlightedLine(fromLine, from.getPositionInLine(), fromLine->chars.length()-2, true); // the last char of every line is \n and we don't want to underline it
663 int skippedLines=0;
664 intrusive_ptr<const Line> currLine = fromLine;
665 while ( (currLine = currLine->getNextLineInBuffer())!= toLine )
666 ++skippedLines;
667 if (skippedLines) {
668 static_pointer_cast<IdeOutputStream>(this->outputStream)->print("// ... (", true, IdeOutputStream::normalFormat);
669 if (skippedLines==1)
670 ios->print("another source line");
671 else
672 ios->print("other ")->print(lexical_cast<string>(skippedLines))->print(" source lines");
673 ios->print(") ...", true, IdeOutputStream::normalFormat);
674 }
675 ios->newlineHL();
676 this->outputHighlightedLine(toLine, 0, to.getPositionInLine(), false);
677 }
678
outputHighlightedLine(intrusive_ptr<const Line> line,size_t from,size_t to,bool keepsHilighting)679 void IdeErrorReporter::outputHighlightedLine(intrusive_ptr<const Line> line, size_t from, size_t to, bool keepsHilighting) {
680 assert(this_thread::get_id()==this->console->interpreterThreadId);
681 const size_t MAX_PREFIX = 40; // these are from the tty version, should we change them?!?
682 const size_t MAX_SUFFIX = 30;
683 assert(dynamic_pointer_cast<IdeOutputStream>(this->outputStream));
684 IdeOutputStream * const ios = static_pointer_cast<IdeOutputStream>(this->outputStream).get();
685 string prefix, suffix("\n");
686 const string &chars = line->chars;
687 size_t printingStart=0, printingEnd=chars.length()==0 ? 0 : chars.length()-1;
688 if (from==to) {
689 if (from>0)
690 --from;
691 if (to<printingEnd)
692 ++to;
693 }
694 if (from>=MAX_PREFIX) {
695 printingStart = from-MAX_PREFIX;
696 prefix = "... ";
697 }
698 if ( (printingEnd-to)>=MAX_SUFFIX ) {
699 printingEnd = to+MAX_SUFFIX;
700 suffix = " ...\n";
701 }
702 assert(printingStart<=printingEnd);
703 assert(printingEnd<chars.length());
704 ios->print(prefix, keepsHilighting, IdeOutputStream::normalFormat);
705 for(size_t i=printingStart; i<=printingEnd;++i) {
706 char c = chars[i];
707 if (c=='\n')
708 break;
709 ios->print(lexical_cast<string>(c=='\t' ? ' ':c), true, (from<=i && i<=to) ? this->highlightErrorFormat : IdeOutputStream::normalFormat);
710 }
711 ios->print(suffix, keepsHilighting, IdeOutputStream::normalFormat);
712 }
713
implReportWarning(const string & msg,const CharPointer & from,const CharPointer & to)714 void IdeErrorReporter::implReportWarning(const string &msg, const CharPointer &from, const CharPointer &to) {
715 assert(this_thread::get_id()==this->console->interpreterThreadId);
716 this->printWarning();
717 this->outputStream->print(msg);
718 this->reportLineNumberWhenMeaningful(from, to, true, true);
719 assert(dynamic_pointer_cast<IdeOutputStream>(this->outputStream));
720 static_pointer_cast<IdeOutputStream>(this->outputStream)->print("\n", true, IdeOutputStream::normalFormat);
721 this->outputHighlightedChars(from, to);
722 }
723
implReportInterrupt(const LexerNS::CharPointer & from,const LexerNS::CharPointer & to)724 void IdeErrorReporter::implReportInterrupt(const LexerNS::CharPointer &from, const LexerNS::CharPointer &to) {
725 assert(this_thread::get_id()==this->console->interpreterThreadId);
726 this->outputStream->print("*** CoCoA-5 Interrupted ***")->newline();
727 this->reportLineNumberWhenMeaningful(from, to, true, true);
728 assert(dynamic_pointer_cast<IdeOutputStream>(this->outputStream));
729 static_pointer_cast<IdeOutputStream>(this->outputStream)->print("\n", true, IdeOutputStream::normalFormat);
730 this->outputHighlightedChars(from, to);
731 }
732
implReportError(const string & msg)733 void IdeErrorReporter::implReportError(const string &msg) {
734 assert(this_thread::get_id()==this->console->interpreterThreadId);
735 this->printError();
736 this->outputStream->print(msg)->newline();
737 }
738
implReportError(const string & msg,const CharPointer & from,const CharPointer & to)739 void IdeErrorReporter::implReportError(const string &msg, const CharPointer &from, const CharPointer &to) {
740 assert(this_thread::get_id()==this->console->interpreterThreadId);
741 this->printError();
742 this->outputStream->print(msg);
743 this->reportLineNumberWhenMeaningful(from, to, true, true);
744 assert(dynamic_pointer_cast<IdeOutputStream>(this->outputStream));
745 static_pointer_cast<IdeOutputStream>(this->outputStream)->print("\n", true, IdeOutputStream::normalFormat);
746 this->outputHighlightedChars(from, to);
747 }
748
print(const string & s,bool highlight,QTextCharFormat format)749 void Console::print(const string &s, bool highlight, QTextCharFormat format) {
750 assert(this_thread::get_id()==Console::guiThreadId);
751 this->outputHL->enabled = highlight;
752 QTextCursor cursor(this->outputTextEdit->document());
753 cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
754 cursor.setCharFormat(format);
755 this->outputTextEdit->setTextCursor(cursor);
756 cursor.insertText(QString::fromStdString(s));
757 this->outputTextEdit->ensureCursorVisible();
758 }
759
onEnterCommandClicked()760 void Console::onEnterCommandClicked()
761 {
762 assert(this_thread::get_id()==Console::guiThreadId);
763 const string input(this->inputTextEdit->toPlainText().toStdString());
764 if (input.length()==0)
765 return;
766 if (this->inputHL->thereIsUnclosedComment())
767 {
768 QMessageBox::critical(this, tr("Unclosed comment"), tr("The command cannot be entered because it contains an unclosed comment"), QMessageBox::Ok, QMessageBox::Ok);
769 return;
770 }
771 if (this->inputHL->thereIsUnclosedStringLiteral())
772 {
773 QMessageBox::critical(this, tr("Unclosed string literal"), tr("The command cannot be entered because it contains an unclosed string literal"), QMessageBox::Ok, QMessageBox::Ok);
774 return;
775 }
776 this->history.push_back(input);
777 if (this->history.size()>static_cast<deque<string>::size_type>(MAX_HISTORY))
778 this->history.pop_front();
779 this->currentHistoryPosition = static_cast<int>(this->history.size());
780 this->clearReportedLocations();
781 this->inputTextEdit->clear();
782 this->inputTextEdit->setFocus();
783 switch(this->interpreter->getStatus())
784 {
785 case Interpreter::IS_WAITING_FOR_COMMAND:
786 case Interpreter::IS_WAITING_FOR_COMMAND_COMPLETION:
787 break;
788 case Interpreter::IS_RUNNING:
789 case Interpreter::IS_RUNNING_BUILTIN:
790 case Interpreter::IS_PAUSED:
791 QMessageBox::warning(this, "Deferred executions", "The interpreter cannot run this command right away; your request has been queued and will be executed ASAP", QMessageBox::Ok);
792 break;
793 case Interpreter::IS_ENDED:
794 QMessageBox::warning(this, "Interpreter is not running anymore", "The interpreter is not running anymore, so it cannot execute anything", QMessageBox::Ok);
795 break;
796 }
797 if (this->debuggerCheckbox->isChecked())
798 this->interpreter->singleStepExecution = true;
799
800 // Input may comprise several lines; pass them separately to lineProvider
801 string::size_type BOL = 0;
802 while (BOL < input.size())
803 {
804 const string::size_type EOL = input.find_first_of('\n', BOL);
805 this->lineProvider->enterLine(input.substr(BOL, EOL-BOL), false);
806 if (EOL == string::npos) break;
807 BOL = EOL+1;
808 }
809 }
810
811
reportLocation(const std::string & filename,int lineNumber,int columnNumber)812 int Console::reportLocation(const std::string &filename, int lineNumber, int columnNumber) {
813 assert(this_thread::get_id()==Console::guiThreadId);
814 BOOST_FOREACH(const ReportedLocation &rl, this->reportedLocations)
815 if (rl.filename==filename && rl.lineNumber==lineNumber && rl.columnNumber==columnNumber)
816 return -1;
817 this->reportedLocations.push_back(ReportedLocation(filename, lineNumber, columnNumber));
818 return static_cast<int>(this->reportedLocations.size())-1;
819 }
820
reportLineNumberWhenMeaningful(const CharPointer & fromPos,const CharPointer &,bool printColumns,bool includeHeader)821 bool IdeErrorReporter::reportLineNumberWhenMeaningful(const CharPointer &fromPos, const CharPointer & /*toPos*/, bool printColumns, bool includeHeader) {
822 assert(this_thread::get_id()==this->console->interpreterThreadId);
823 bool result = this->ErrorReporter::reportLineNumberWhenMeaningful(fromPos, fromPos, printColumns, includeHeader);
824 if (result) {
825 const intrusive_ptr<const Line> line = fromPos.getLine();
826 const intrusive_ptr<const FileLineProvider> p = intrusive_ptr_cast<const FileLineProvider>(line->provider);
827 this->console->postCommand(new ReportErrorQC(p->myFileName(), line->number, fromPos.getPositionInLine()));
828 }
829 return result;
830 }
831
execute(Console * console)832 void ReportErrorQC::execute(Console *console) {
833 assert(this_thread::get_id()==Console::guiThreadId);
834 const int index = console->reportLocation(this->filename, this->lineNumber, this->columnNumber);
835 if (index>=0) {
836 console->locationComboBox->setEnabled(true);
837 assert(this->lineNumber>=1);
838 const string strippedFilename(QFileInfo(QString::fromStdString(this->filename)).baseName().toStdString());
839 console->locationComboBox->addItem(QString::fromStdString("Line "+lexical_cast<string>(this->lineNumber)+" (col. "+ lexical_cast<string>(this->columnNumber+1) +") of "+strippedFilename), QVariant(index));
840 console->openInEditorButton->setEnabled(true);
841 }
842 }
843
clearReportedLocations()844 void Console::clearReportedLocations() {
845 assert(this_thread::get_id()==Console::guiThreadId);
846 this->reportedLocations.clear();
847 this->openInEditorButton->setEnabled(false);
848 this->locationComboBox->clear();
849 this->locationComboBox->setEnabled(false);
850 }
851
postCommand(intrusive_ptr<QueuedCommand> command)852 void Console::postCommand(intrusive_ptr<QueuedCommand> command) {
853 int nCommands;
854 {
855 lock_guard<mutex> lock(this->mut);
856 this->commands.push_back(command);
857 nCommands = this->commands.size();
858 }
859 const int tooManyQueuedCommands = /* arbitrarily set to... */ 16;
860 if (nCommands>tooManyQueuedCommands && this_thread::get_id()==this->interpreterThreadId)
861 this_thread::sleep(boost::posix_time::milliseconds(25));
862 }
863
eventFilter(QObject * target,QEvent * event)864 bool Console::eventFilter(QObject *target, QEvent *event) {
865 assert(this_thread::get_id()==Console::guiThreadId);
866 if (target == this->inputTextEdit && event->type() == QEvent::KeyPress) {
867 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
868 const int key = keyEvent->key();
869 if ((keyEvent->modifiers() & Qt::ControlModifier)==Qt::ControlModifier) {
870 if (key==Qt::Key_Up) {
871 if (this->currentHistoryPosition>0) {
872 this->inputTextEdit->setText(QString::fromStdString(this->history[--this->currentHistoryPosition]));
873 goto cursorAtTheEnd;
874 }
875 return true;
876 }
877 if (key==Qt::Key_Down) {
878 if (this->currentHistoryPosition < static_cast<int>((this->history.size()-1))) {
879 this->inputTextEdit->setText(QString::fromStdString(this->history[++this->currentHistoryPosition]));
880 cursorAtTheEnd:
881 QTextCursor cursor(this->inputTextEdit->document());
882 cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
883 this->inputTextEdit->setTextCursor(cursor);
884 }
885 return true;
886 }
887 } else if (key == Qt::Key_Return) {
888 this->onEnterCommandClicked();
889 return true;
890 }
891 }
892 return QWidget::eventFilter(target, event);
893 }
894
clearOutputWindow(bool AskUser)895 void Console::clearOutputWindow(bool AskUser) {
896 assert(this_thread::get_id()==Console::guiThreadId);
897 if (!AskUser || QMessageBox::question(this, tr("Deletion confirmation"), tr("Are you sure you want to delete the contents of the output window?"),
898 QMessageBox::Yes|QMessageBox::No, QMessageBox::No)==QMessageBox::Yes) {
899 this->outputTextEdit->clear();
900 }
901 }
902
execute(Console * console)903 void PrintQC::execute(Console *console) {
904 assert(this_thread::get_id()==Console::guiThreadId);
905 console->print(this->s, this->highlight, this->format);
906 }
907
908 namespace {
909 class RunTheInterpreter {
910 intrusive_ptr<Interpreter> interpreter;
911 Console *console;
912 public:
RunTheInterpreter(Console * console,intrusive_ptr<Interpreter> interpreter)913 explicit RunTheInterpreter(Console *console, intrusive_ptr<Interpreter> interpreter) :
914 interpreter(interpreter),
915 console(console)
916 {}
operator()917 void operator()() {
918 this->console->interpreterThreadId = this_thread::get_id();
919 this->interpreter->run(console);
920 }
921 };
922 }
923
Console(QWidget * parent,MainWindow * mainWindow,WarningSeverity warningLevel,bool warnAboutCocoa5,const vector<string> & packageList,bool fullCoCoALibError)924 Console::Console(QWidget *parent, MainWindow *mainWindow, WarningSeverity warningLevel, bool warnAboutCocoa5, const vector<string> & packageList, bool fullCoCoALibError) :
925 QWidget(parent),
926 currentHistoryPosition(-1),
927 mainWindow(mainWindow),
928 packageList(packageList),
929 outputStream(new IdeOutputStream(this)),
930 lineProvider(new IdeLineProvider(this)),
931 errorReporter(new IdeErrorReporter(warningLevel, this, this->outputStream))
932 {
933 guiThreadId = this_thread::get_id();
934 this->setupUi(this);
935 this->inputHL = new CocoaHighlighter(this->inputTextEdit->document(), true, true);
936 this->outputHL = new CocoaHighlighter(this->outputTextEdit->document(), false, false);
937 this->setHighlighterFormats();
938 this->outputHL->enabled = false;
939 connect(this->enterButton, SIGNAL(clicked()), this, SLOT(onEnterCommandClicked()));
940 connect(this->clearOutputButton, SIGNAL(clicked()), this, SLOT(clearOutputWindow()));
941 this->inputTextEdit->installEventFilter(this);
942 this->interpreter = new Interpreter(warnAboutCocoa5, this->lineProvider, this->errorReporter, this->outputStream, fullCoCoALibError, 5000); // 5000 is "arbitrary" MaxStackSize
943 boost::thread intThread(RunTheInterpreter(this, this->interpreter));
944 QTimer *timer = new QTimer(this);
945 connect(timer, SIGNAL(timeout()), this, SLOT(processCommands()));
946 timer->start(20);
947 connect(this->openInEditorButton, SIGNAL(clicked()), this, SLOT(onOpenInEditorClicked()));
948 connect(this->pauseButton, SIGNAL(clicked()), this, SLOT(onPauseClicked()));
949 connect(this->interruptButton, SIGNAL(clicked()), this, SLOT(onInterruptClicked()));
950 this->packageLoadingProgressBar->setMinimum(0);
951 this->packageLoadingProgressBar->setMaximum(packageList.size()+1);
952 this->packageLoadingProgressBar->setValue(0);
953 }
954
onOpenInEditorClicked()955 void Console::onOpenInEditorClicked() {
956 assert(this_thread::get_id()==Console::guiThreadId);
957 int currentIndex = this->locationComboBox->currentIndex();
958 if (currentIndex<0)
959 return;
960 QVariant variant = this->locationComboBox->itemData(currentIndex);
961 assert(variant.type()==QVariant::Int);
962 int i = variant.toInt();
963 assert(0<=i && i<static_cast<int>(this->reportedLocations.size()));
964 QFileInfo file(QString::fromStdString(this->reportedLocations[i].filename));
965 if (!file.exists()) {
966 QMessageBox::critical(this, tr("File not found"), tr("Cannot find the selected file"), QMessageBox::Ok);
967 return;
968 }
969 QString absPath(file.absoluteFilePath());
970 SourceEditor *sourceEditor = this->mainWindow->editorFor(absPath, true);
971 if (!sourceEditor) {
972 sourceEditor = this->mainWindow->onFileNew();
973 sourceEditor->load(absPath);
974 }
975 QEditor *const editor = sourceEditor->getEditor();
976 const int lineNumber = this->reportedLocations[i].lineNumber;
977 const int columnNumber = this->reportedLocations[i].columnNumber;
978 assert(lineNumber>=1);
979 editor->setCursor(QDocumentCursor(editor->document(), lineNumber - 1, columnNumber));
980 }
981
editorFor(QString filename,bool activateWindow)982 SourceEditor *MainWindow::editorFor(QString filename, bool activateWindow) {
983 assert(this_thread::get_id()==Console::guiThreadId);
984 QString absPath = QFileInfo(filename).absoluteFilePath();
985 QList<QMdiSubWindow *> wList = this->mdiArea->subWindowList();
986 for (int i = 0; i < wList.size(); ++i) {
987 QMdiSubWindow *const win = wList.at(i);
988 SourceEditor *ed = dynamic_cast<SourceEditor *>(win->widget());
989 if (ed && QFileInfo(ed->getEditor()->fileName()).absoluteFilePath()==absPath) {
990 if (activateWindow) {
991 win->raise();
992 ed->getEditor()->setFocus();
993 }
994 return ed;
995 }
996 }
997 return 0;
998 }
999
closeEvent(QCloseEvent * e)1000 void MainWindow::closeEvent(QCloseEvent *e) {
1001 assert(this_thread::get_id()==Console::guiThreadId);
1002 if (this->interpreter->getStatus()!=Interpreter::IS_ENDED &&
1003 QMessageBox::question(
1004 this,
1005 tr("Exit confirmation"),
1006 tr("Are you sure you want to quit C5?"),
1007 QMessageBox::Yes|QMessageBox::No,
1008 QMessageBox::No)==QMessageBox::No) {
1009 e->ignore();
1010 return;
1011 }
1012 this->console->clearOutputWindow(false); // false -> do not ask for confirmation
1013 this->statusLabel->setText(" Exiting... "); // next cmd makes the update appear
1014 QMessageBox::information(this, "Ciao", "Ciao :-)");
1015 this->mdiArea->closeAllSubWindows();
1016 if (mdiArea->subWindowList().size()!=2 /* that is, the console and the debugger */)
1017 e->ignore();
1018 else {
1019 if (this->interpreter->getStatus()==Interpreter::IS_ENDED)
1020 QMessageBox::information(this, "Ciao", "Ciao :-)");
1021 e->accept();
1022 }
1023 }
1024
findMainWindow(QObject * o)1025 MainWindow *MainWindow::findMainWindow(QObject *o) {
1026 assert(this_thread::get_id()==Console::guiThreadId);
1027 while (o) {
1028 if (MainWindow *mw = dynamic_cast<MainWindow *>(o))
1029 return mw;
1030 o = o->parent();
1031 assert(o);
1032 }
1033 return 0; // to make the compiler happy
1034 }
1035
findConsole(QObject * w)1036 Console *MainWindow::findConsole(QObject *w) {
1037 assert(this_thread::get_id()==Console::guiThreadId);
1038 return findMainWindow(w)->console;
1039 }
1040
processCommands()1041 void Console::processCommands() {
1042 assert(this_thread::get_id()==Console::guiThreadId);
1043 this->mainWindow->updateStatusLabel(); // it's important to do this before executing the commands (resume() uses flush to sync)
1044 lock_guard<mutex> lock(this->mut);
1045 while (!this->commands.empty()) {
1046 this->commands.front()->execute(this);
1047 this->commands.pop_front();
1048 }
1049 }
1050
1051 QTextCharFormat IdeOutputStream::normalFormat;
1052 QTextCharFormat IdeOutputStream::boldFormat;
1053
printCalledBy()1054 void IdeErrorReporter::printCalledBy() {
1055 assert(this_thread::get_id()==this->console->interpreterThreadId);
1056 assert(dynamic_pointer_cast<IdeOutputStream>(this->outputStream));
1057 static_pointer_cast<IdeOutputStream>(this->outputStream)->print(CalledbyPrefix, false, this->calledByFormat);
1058 }
1059
printWhere()1060 void IdeErrorReporter::printWhere() {
1061 assert(this_thread::get_id()==this->console->interpreterThreadId);
1062 assert(dynamic_pointer_cast<IdeOutputStream>(this->outputStream));
1063 static_pointer_cast<IdeOutputStream>(this->outputStream)->print(WherePrefix, false, this->whereFormat);
1064 }
1065
printBold(const string & s)1066 void IdeErrorReporter::printBold(const string &s) {
1067 assert(this_thread::get_id()==this->console->interpreterThreadId);
1068 assert(dynamic_pointer_cast<IdeOutputStream>(this->outputStream));
1069 static_pointer_cast<IdeOutputStream>(this->outputStream)->print(s, false, this->boldFormat);
1070 }
1071
printWarning()1072 void IdeErrorReporter::printWarning() {
1073 assert(this_thread::get_id()==this->console->interpreterThreadId);
1074 assert(dynamic_pointer_cast<IdeOutputStream>(this->outputStream));
1075 static_pointer_cast<IdeOutputStream>(this->outputStream)->print(WarningPrefix, false, this->warningFormat);
1076 }
1077
printError()1078 void IdeErrorReporter::printError() {
1079 assert(this_thread::get_id()==this->console->interpreterThreadId);
1080 assert(dynamic_pointer_cast<IdeOutputStream>(this->outputStream));
1081 static_pointer_cast<IdeOutputStream>(this->outputStream)->print(ErrorPrefix, false, this->errorFormat);
1082 }
1083
printContext()1084 void IdeErrorReporter::printContext() {
1085 assert(this_thread::get_id()==this->console->interpreterThreadId);
1086 assert(dynamic_pointer_cast<IdeOutputStream>(this->outputStream));
1087 static_pointer_cast<IdeOutputStream>(this->outputStream)->print(ContextPrefix, false, this->contextFormat);
1088 }
1089
1090 namespace {
1091
1092 class PageUpDownCommand : public QEditorInputBinding::Command {
1093 const bool up;
1094 const QDocumentCursor::MoveMode mode;
1095 public:
PageUpDownCommand(bool up,QDocumentCursor::MoveMode mode)1096 PageUpDownCommand(bool up, QDocumentCursor::MoveMode mode) :
1097 up(up),
1098 mode(mode)
1099 {}
exec(QEditor * e)1100 void exec(QEditor *e) {
1101 QDocumentCursor::MoveMode mode = this->mode;
1102 if ( e->flag(QEditor::LineWrap) && e->flag(QEditor::CursorJumpPastWrap) )
1103 mode |= QDocumentCursor::ThroughWrap;
1104 if (up)
1105 e->pageUp(mode);
1106 else
1107 e->pageDown(mode);
1108 }
1109 };
1110
1111 class EmacsBinding : public QEditorInputBinding {
id()1112 QString id() const { return "emacs binding"; }
name()1113 QString name() const { return this->id(); }
1114 public:
EmacsBinding()1115 EmacsBinding() {
1116 this->setMapping(QKeySequence("Ctrl+A"), new QEditorInputBinding::MotionCommand(QDocumentCursor::StartOfLine, QDocumentCursor::MoveAnchor, 1));
1117 this->setMapping(QKeySequence("Ctrl+E"), new QEditorInputBinding::MotionCommand(QDocumentCursor::EndOfLine, QDocumentCursor::MoveAnchor, 1));
1118 this->setMapping(QKeySequence("Ctrl+V"), new PageUpDownCommand(false, QDocumentCursor::MoveAnchor));
1119 this->setMapping(QKeySequence("Ctrl+Shift+V"), new PageUpDownCommand(false, QDocumentCursor::KeepAnchor));
1120 this->setMapping(QKeySequence("Meta+V"), new PageUpDownCommand(true, QDocumentCursor::MoveAnchor));
1121 this->setMapping(QKeySequence("Meta+Shift+V"), new PageUpDownCommand(true, QDocumentCursor::KeepAnchor));
1122 // to add:
1123 // Meta+W copy
1124 // Ctrl+W cut
1125 // Ctrl+Y paste
1126 // Ctrl+_ undo
1127 // Ctrl+S search
1128 // Ctrl+X, Ctrl+S save
1129 }
1130 };
1131
1132 }
1133
applyTo(QFormatScheme * fScheme)1134 void ColorScheme::applyTo(QFormatScheme *fScheme) const {
1135 fScheme->setFormat("stringLiteral", this->stringLiterals);
1136 fScheme->setFormat("constant", this->constants);
1137 fScheme->setFormat("comment", this->comments);
1138 fScheme->setFormat("keyword", this->keywords);
1139 fScheme->setFormat("type", this->types);
1140 fScheme->setFormat("numericLiteral", this->numericalLiterals);
1141 fScheme->setFormat("unknownChar", this->unknownChars);
1142 fScheme->setFormat("braceMatch", this->braceMatch);
1143 fScheme->setFormat("braceMismatch", this->braceMismatch);
1144 }
1145
SourceEditor(MainWindow * parent)1146 SourceEditor::SourceEditor(MainWindow *parent) :
1147 QWidget(parent),
1148 menuAction(new QAction(this))
1149 {
1150 assert(this_thread::get_id()==Console::guiThreadId);
1151 this->setupUi(this);
1152 const bool emacsLike = parent->actionEmacsLike->isChecked();
1153 this->codeEdit = new QCodeEdit(!emacsLike, this);
1154 QEditor * const ed = this->getEditor();
1155 if (emacsLike) {
1156 ed->addInputBinding(new EmacsBinding());
1157 }
1158 //this->codeEdit->addPanel("Line Mark Panel", QCodeEdit::West, true)->setShortcut(QKeySequence("F6"));
1159 //this->codeEdit->addPanel("Line Number Panel", QCodeEdit::West, true)->setShortcut(QKeySequence("F11"));
1160 //this->codeEdit->addPanel("Fold Panel", QCodeEdit::West, true)->setShortcut(QKeySequence("F9"));
1161 this->codeEdit->addPanel("Line Change Panel", QCodeEdit::West, true);
1162 this->codeEdit->addPanel("Status Panel", QCodeEdit::South, true);
1163 this->codeEdit->addPanel("Goto Line Panel", QCodeEdit::South);
1164 this->codeEdit->addPanel("Search Replace Panel", QCodeEdit::South);
1165 this->outerVL->addWidget(ed);
1166 QFormatScheme *fScheme = new QFormatScheme(this);
1167 parent->getCurrentColorScheme()->applyTo(fScheme);
1168 QLanguageFactory *lFactory = new QLanguageFactory(fScheme, this);
1169 lFactory->addDefinitionPath(":/qxs");
1170 lFactory->setLanguage(this->getEditor(), "a.cocoa5");
1171 connect(this->saveButton, SIGNAL(clicked()), this, SLOT(save()));
1172 connect(this->saveAsButton, SIGNAL(clicked()), this, SLOT(saveAs()));
1173 connect(this->saveAndRunButton, SIGNAL(clicked()), this, SLOT(saveAndRun()));
1174 connect(ed, SIGNAL(titleChanged(const QString&)), this, SLOT(onEditorTitleChanged(const QString&)));
1175 connect(ed, SIGNAL(contentModified(bool)), this, SLOT(onEditorContentModified(bool)));
1176 ed->setTitle("unnamed [*]");
1177 this->menuAction->setCheckable(true);
1178 connect(this->menuAction, SIGNAL(triggered()), ed, SLOT(setFocus()));
1179 }
1180
onFileNew()1181 SourceEditor *MainWindow::onFileNew() {
1182 assert(this_thread::get_id()==Console::guiThreadId);
1183 SourceEditor *sourceEditor = new SourceEditor(this);
1184 this->menuWindow->addAction(sourceEditor->menuAction);
1185 this->menuWindowActionGroup->addAction(sourceEditor->menuAction);
1186 QMdiSubWindow * const win = this->mdiArea->addSubWindow(sourceEditor);
1187 win->setAttribute(Qt::WA_DeleteOnClose);
1188 win->showNormal();
1189 return sourceEditor;
1190 }
1191
onEditorTitleChanged(const QString & title)1192 void SourceEditor::onEditorTitleChanged(const QString& title) {
1193 assert(this_thread::get_id()==Console::guiThreadId);
1194 this->setWindowTitle(title);
1195 QString fn = this->getEditor()->fileName();
1196 if (!fn.size())
1197 fn = "unnamed";
1198 this->menuAction->setText(fn);
1199 }
1200
onEditorContentModified(bool y)1201 void SourceEditor::onEditorContentModified(bool y) {
1202 assert(this_thread::get_id()==Console::guiThreadId);
1203 this->setWindowModified(y);
1204 }
1205
load(const QString & file)1206 void SourceEditor::load(const QString& file) {
1207 assert(this_thread::get_id()==Console::guiThreadId);
1208 QEditor *const ed = this->getEditor();
1209 QString filename = file.count() ? QFileInfo(file).absoluteFilePath() : file;
1210 if (filename.size() && QFile::exists(filename)) {
1211 ed->load(filename);
1212 // TODO: do something for recent files...
1213 // updateRecentFiles(filename);
1214 this->getEditor()->setTitle(QString("%1 [*]").arg(filename));
1215 } else {
1216 ed->setFileName("");
1217 ed->setText("");
1218 this->getEditor()->setTitle("unnamed [*]");
1219 }
1220 }
1221
save()1222 void SourceEditor::save() {
1223 assert(this_thread::get_id()==Console::guiThreadId);
1224 QEditor *const ed = this->getEditor();
1225 if (ed->fileName().size())
1226 ed->save();
1227 else
1228 this->saveAs();
1229 }
1230
saveAndRun()1231 void SourceEditor::saveAndRun() {
1232 assert(this_thread::get_id()==Console::guiThreadId);
1233 this->save();
1234 QEditor *const ed = this->getEditor();
1235 if (ed->isContentModified())
1236 return;
1237 ostringstream ss;
1238 intrusive_ptr<STRING> s(new STRING(ed->fileName().toStdString()));
1239 s->dumpAsString(ss);
1240 Console *console = MainWindow::findConsole(this);
1241 if (console->interpreter->getStatus()!=Interpreter::IS_WAITING_FOR_COMMAND)
1242 QMessageBox::warning(this, "Deferred executions", "The interpreter cannot run this file right away; your request has been queued and will be executed ASAP", QMessageBox::Ok);
1243 console->lineProvider->enterLine(this->debuggerCheckbox->isChecked() ? "debug("+ss.str()+") ;" : "Source "+ss.str()+";", true);
1244 }
1245
onFileSaveOutputWindow()1246 void MainWindow::onFileSaveOutputWindow() {
1247 QString fn = QFileDialog::getSaveFileName(this, tr("Save output-windows contents as..."));
1248 if (fn.isEmpty())
1249 return;
1250 QFile file(fn);
1251 if (!file.open(QIODevice::WriteOnly)) {
1252 QMessageBox::critical(this, "Cannot write", "Cannot open the file for writing", QMessageBox::Ok);
1253 return;
1254 }
1255 if (file.write(this->console->outputTextEdit->toPlainText().toUtf8()) < 0)
1256 QMessageBox::critical(this, "Cannot write", "Error while writing to the file", QMessageBox::Ok);
1257 file.close();
1258 }
1259
saveAs()1260 void SourceEditor::saveAs() {
1261 assert(this_thread::get_id()==Console::guiThreadId);
1262 QEditor *const ed = this->getEditor();
1263 QString fn;
1264 for(;;) {
1265 fn = QFileDialog::getSaveFileName(this, tr("Save file as..."), ed->fileName(), tr("Sources (*.cocoa5 *.cpkg5)"));
1266 if (fn.isEmpty())
1267 return;
1268 if (!(fn.endsWith(tr(".cocoa5")) || fn.endsWith(tr(".cpkg5"))))
1269 fn.append(".cocoa5");
1270 SourceEditor *otherEditor = MainWindow::findMainWindow(this)->editorFor(fn, false);
1271 if (otherEditor==0 || otherEditor==this)
1272 break;
1273 QMessageBox::critical(this, "Name already in use", "This filename is already used by another edit-window; please choose another name", QMessageBox::Ok);
1274 }
1275 ed->save(fn);
1276 this->setWindowTitle(QString("[%1[*]]").arg(fn));
1277 }
1278
maybeSave()1279 bool SourceEditor::maybeSave() {
1280 assert(this_thread::get_id()==Console::guiThreadId);
1281 QEditor *const ed = this->getEditor();
1282 if (ed->isContentModified()) {
1283 int ret = QMessageBox::warning(
1284 this,
1285 tr("About to close"),
1286 tr("The file %1 contains unsaved modifications.\nWould you like to save it?").arg(ed->fileName()),
1287 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes);
1288 if (ret == QMessageBox::Cancel)
1289 return true;
1290 else if (ret == QMessageBox::Yes)
1291 ed->save();
1292 }
1293 return false;
1294 }
1295
closeEvent(QCloseEvent * e)1296 void SourceEditor::closeEvent(QCloseEvent *e) {
1297 assert(this_thread::get_id()==Console::guiThreadId);
1298 if ( this->maybeSave() ) {
1299 e->ignore();
1300 return;
1301 }
1302 this->QWidget::closeEvent(e);
1303 }
1304
setHighlighterFormats()1305 void Debugger::setHighlighterFormats() {
1306 this->codeHL->setFormats(*this->mainWindow->getCurrentColorScheme());
1307 this->codeHL->rehighlight();
1308 }
1309
Debugger(QWidget * parent,MainWindow * mainWindow)1310 Debugger::Debugger(QWidget *parent, MainWindow *mainWindow) :
1311 QWidget(parent),
1312 mainWindow(mainWindow)
1313 {
1314 this->setupUi(this);
1315 this->codeHL = new CocoaHighlighter(this->codeTextEdit->document(), false, true);
1316 this->setHighlighterFormats();
1317 connect(this->stepIntoButton, SIGNAL(clicked()), this, SLOT(onStepIntoClicked()));
1318 connect(this->stepOverButton, SIGNAL(clicked()), this, SLOT(onStepOverClicked()));
1319 connect(this->stepOutButton, SIGNAL(clicked()), this, SLOT(onStepOutClicked()));
1320 connect(this->stepOutFnProcButton, SIGNAL(clicked()), this, SLOT(onStepOutFnProcClicked()));
1321 connect(this->continueButton, SIGNAL(clicked()), this, SLOT(onContinueClicked()));
1322 connect(this->stopButton, SIGNAL(clicked()), this, SLOT(onStopClicked()));
1323 this->format.setBackground(QColor(255, 239, 0, 255));
1324 connect(this->callStackList, SIGNAL(itemSelectionChanged()), this, SLOT(onCallStackSelectionChanged()));
1325 connect(this->hideExportedNamesCheckBox, SIGNAL(stateChanged(int)), this, SLOT(onHidingChoicesChanged()));
1326 connect(this->hideFunctionsCheckBox, SIGNAL(stateChanged(int)), this, SLOT(onHidingChoicesChanged()));
1327 connect(this->hideSystemNamesCheckBox, SIGNAL(stateChanged(int)), this, SLOT(onHidingChoicesChanged()));
1328 connect(this->hideTypesCheckBox, SIGNAL(stateChanged(int)), this, SLOT(onHidingChoicesChanged()));
1329 connect(this->autoFillLocalsCheckBox, SIGNAL(stateChanged(int)), this, SLOT(onAutoFillLocalsStateChanged()));
1330 connect(this->fillLocalsButton, SIGNAL(clicked()), this, SLOT(onFillLocalsClicked()));
1331 }
1332
onFillLocalsClicked()1333 void Debugger::onFillLocalsClicked() {
1334 this->fillLocalsButton->setEnabled(false);
1335 this->fillLocals();
1336 }
1337
onAutoFillLocalsStateChanged()1338 void Debugger::onAutoFillLocalsStateChanged() {
1339 if (this->autoFillLocalsCheckBox->isChecked()) {
1340 this->fillLocals();
1341 this->fillLocalsButton->setEnabled(false);
1342 }
1343 else
1344 this->fillLocalsButton->setEnabled(true);
1345 }
1346
onCallStackSelectionChanged()1347 void Debugger::onCallStackSelectionChanged() {
1348 const int cRow = this->callStackList->currentRow();
1349 const bool isTopLevel = cRow==(this->callStackList->count()-1);
1350 const bool interpreterPaused = this->interpreter->getStatus() == Interpreter::IS_PAUSED;
1351 this->stepIntoButton->setEnabled(interpreterPaused && cRow==0);
1352 this->stepOverButton->setEnabled(interpreterPaused && cRow==0);
1353 this->stepOutFnProcButton->setEnabled(interpreterPaused && cRow==0 && !isTopLevel);
1354 this->stepOutButton->setEnabled(interpreterPaused && cRow==0);
1355 this->continueButton->setEnabled(interpreterPaused);
1356 this->stopButton->setEnabled(interpreterPaused);
1357 this->hideExportedNamesCheckBox->setEnabled(isTopLevel);
1358 this->hideSystemNamesCheckBox->setEnabled(isTopLevel);
1359 this->fillCode();
1360 if (this->autoFillLocalsCheckBox->isChecked()) {
1361 this->fillLocalsButton->setEnabled(false);
1362 this->fillLocals();
1363 } else {
1364 this->fillLocalsButton->setEnabled(true);
1365 this->localsTree->clear();
1366 }
1367 }
1368
onHidingChoicesChanged()1369 void Debugger::onHidingChoicesChanged() {
1370 if (this->autoFillLocalsCheckBox->isChecked())
1371 this->fillLocals();
1372 else
1373 this->fillLocalsButton->setEnabled(true);
1374 }
1375
addLocal(const string & name,const StaticEnv::VarData & varData,const Frame * currentFrame)1376 void Debugger::addLocal(const string &name, const StaticEnv::VarData &varData, const Frame *currentFrame) {
1377 if (varData.depth<0)
1378 return;
1379 const QString qname(QString::fromStdString(name));
1380 RuntimeEnvironment &re = this->interpreter->runtimeEnvironment;
1381 const Frame *f = varData.debuggerTryToFindFrame(re.getTopLevelFrame(), currentFrame);
1382 if (!f) {
1383 this->addLocalError(qname, "Its environment does not exist anymore");
1384 return;
1385 }
1386 if (varData.isCapturedValue) {
1387 assert(f->userdefinedFun);
1388 assert(static_cast<vector<intrusive_ptr<RightValue> >::size_type>(varData.index) < f->userdefinedFun->capturedValues.size());
1389 const intrusive_ptr<RightValue> v(f->userdefinedFun->capturedValues[varData.index]);
1390 if (dynamic_pointer_cast<FUNCTION>(v) && this->hideFunctionsCheckBox->isChecked())
1391 return;
1392 QTreeWidgetItem *item = new QTreeWidgetItem(this->localsTree);
1393 item->setText(COL_NAME, qname);
1394 item->setText(COL_KIND, "captured");
1395 v->initTreeWidget(item);
1396 this->localsTree->addTopLevelItem(item);
1397 return;
1398 }
1399 this->addLocal(name, f, varData.index);
1400 }
1401
addLocal(const std::string & name,const InterpreterNS::Frame * f,int index)1402 void Debugger::addLocal(const std::string &name, const InterpreterNS::Frame *f, int index) {
1403 QString qname(QString::fromStdString(name));
1404 assert(index>=0 && index<static_cast<int>(f->varSlots.size()));
1405 const VariableSlot &vs = f->varSlots[index];
1406 if (vs.isSystemProtected() && !vs.isIterationVariable() && this->hideSystemNamesCheckBox->isEnabled() && this->hideSystemNamesCheckBox->isChecked())
1407 return;
1408 if (vs.isPackage() && this->hideExportedNamesCheckBox->isEnabled() && this->hideExportedNamesCheckBox->isChecked())
1409 return;
1410 intrusive_ptr<Value> v = vs.value;
1411 if (!v) { // this can only happen when a package has been reloaded (and some of the previous version members are not defined anymore) or an indeterminate has been removed because of a Use statement
1412 assert(f==this->interpreter->runtimeEnvironment.getTopLevelFrame());
1413 return;
1414 }
1415 intrusive_ptr<RightValue> rv;
1416 try {
1417 rv = v->asRightValue();
1418 } catch (const InterruptException &) {
1419 this->addLocalError(qname, "Evaluation has been interrupted");
1420 return;
1421 } catch (const RuntimeException &) {
1422 this->addLocalError(qname, "Reference cannot be evaluated");
1423 return;
1424 }
1425 if (dynamic_pointer_cast<TYPE>(rv) && this->hideTypesCheckBox->isChecked())
1426 return;
1427 if (dynamic_pointer_cast<FUNCTION>(rv) && this->hideFunctionsCheckBox->isChecked())
1428 return;
1429 QTreeWidgetItem *item = new QTreeWidgetItem(this->localsTree);
1430 item->setText(COL_NAME, qname);
1431 QString kind;
1432 if (vs.isIterationVariable())
1433 kind = "iteration";
1434 else if (vs.isPackage())
1435 kind = "package export";
1436 else if (vs.isSystemProtected())
1437 kind = "system";
1438 else {
1439 if (dynamic_pointer_cast<LeftValue>(v))
1440 kind = vs.isProtected() ? "user protected, reference" : "reference";
1441 else if (vs.isProtected())
1442 kind = "user protected";
1443 }
1444 item->setText(COL_KIND, kind);
1445 rv->initTreeWidget(item);
1446 this->localsTree->addTopLevelItem(item);
1447 }
1448
addLocalError(const QString & name,const QString & message)1449 void Debugger::addLocalError(const QString &name, const QString &message) {
1450 QTreeWidgetItem *item = new QTreeWidgetItem(this->localsTree);
1451 item->setText(COL_NAME, name);
1452 item->setText(COL_VALUE, message);
1453 this->localsTree->addTopLevelItem(item);
1454 }
1455
update()1456 void Debugger::update() {
1457 switch (this->interpreter->getStatus()) {
1458 case Interpreter::IS_WAITING_FOR_COMMAND:
1459 case Interpreter::IS_WAITING_FOR_COMMAND_COMPLETION:
1460 case Interpreter::IS_PAUSED:
1461 case Interpreter::IS_ENDED:
1462 this->fillCallStack();
1463 this->setEnabled(true);
1464 break;
1465 case Interpreter::IS_RUNNING:
1466 case Interpreter::IS_RUNNING_BUILTIN:
1467 this->setEnabled(false);
1468 return;
1469 }
1470 }
1471
fillLocals()1472 void Debugger::fillLocals() {
1473 this->localsTree->clear();
1474 if (this->listedFrames.size()==0)
1475 return;
1476 int selected = this->callStackList->currentRow();
1477 assert(selected>=0 && selected<static_cast<int>(this->listedFrames.size()));
1478 RuntimeEnvironment &re = this->interpreter->runtimeEnvironment;
1479 const Frame *const tlFrame = re.getTopLevelFrame();
1480 const Frame *f = selected==0 ? re.getCurrentFrame() : (this->listedFrames[selected-1]-1);
1481 for(;f!=tlFrame;--f) {
1482 assert(f>tlFrame);
1483 intrusive_ptr<StaticEnv> env = f->block->staticEnv;
1484 assert(env);
1485 assert(!f->userdefinedFun || f->userdefinedFun->fnDecl->staticEnv==f->block->staticEnv);
1486 for(StaticEnv::IdMap::const_iterator it = env->identifierMap.begin(); it!=env->identifierMap.end(); ++it)
1487 this->addLocal(it->first, it->second, f);
1488 if (f->userdefinedFun)
1489 break;
1490 }
1491 if (f==tlFrame) {
1492 for(map<string, int>::const_iterator it = re.topLevelIdentifiers.begin(); it!=re.topLevelIdentifiers.end(); ++it) {
1493 assert(it->first.length());
1494 assert(it->second>=0 && it->second<static_cast<int>(tlFrame->varSlots.size()));
1495 if (it->first[0]!='$')
1496 this->addLocal(it->first, f, it->second);
1497 }
1498 }
1499 }
1500
fillCode()1501 void Debugger::fillCode() {
1502 if (this->listedFrames.size()==0 || this->interpreter->getStatus()!=Interpreter::IS_PAUSED) {
1503 this->codeTextEdit->clear();
1504 this->codeGroupBox->setTitle("Code");
1505 return;
1506 }
1507 int selected = this->callStackList->currentRow();
1508 assert(selected>=0 && selected<static_cast<int>(this->listedFrames.size()));
1509 CharPointer begin(this->interpreter->pausedOn->getBegin());
1510 CharPointer end(this->interpreter->pausedOn->getEnd());
1511 if (selected) {
1512 const Frame *f = this->listedFrames[selected-1];
1513 assert(f->userdefinedFun);
1514 assert(f->invocationExp);
1515 begin = f->invocationExp->getBegin();
1516 end = f->invocationExp->getEnd();
1517 }
1518 const QTextCharFormat oldFormat = this->codeTextEdit->currentCharFormat();
1519 const intrusive_ptr<const LineProvider> provider = begin.getLine()->provider;
1520 if (provider->IamReadingFromFile()) {
1521 intrusive_ptr<const FileLineProvider> fileProvider = dynamic_pointer_cast<const FileLineProvider>(begin.getLine()->provider); // NASTY HACK
1522 this->codeGroupBox->setTitle(QString::fromStdString(provider->myFileName()));
1523 this->codeTextEdit->setText(QString::fromStdString(fileProvider->wholeFile));
1524 QTextCursor c(this->codeTextEdit->document());
1525 c.setPosition(0);
1526 const int beginLineNumber = begin.getLine()->number;
1527 const int endLineNumber = end.getLine()->number;
1528 assert(beginLineNumber>=1);
1529 assert(endLineNumber>=beginLineNumber);
1530 c.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, beginLineNumber-1);
1531 c.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, static_cast<int>(begin.getPositionInLine()));
1532 c.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
1533 c.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor, endLineNumber - beginLineNumber);
1534 c.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, static_cast<int>(end.getPositionInLine())+1);
1535 c.mergeCharFormat(this->format);
1536 c.clearSelection();
1537 c.setCharFormat(oldFormat);
1538 this->codeTextEdit->setTextCursor(c);
1539 } else {
1540 this->codeGroupBox->setTitle("Top-level code");
1541 this->codeTextEdit->clear();
1542 intrusive_ptr<const Line> line = begin.getLine();
1543 this->codeTextEdit->append(QString::fromStdString(line->chars));
1544 QTextCursor c(this->codeTextEdit->document());
1545 c.setPosition(0);
1546 c.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, static_cast<int>(begin.getPositionInLine()));
1547 c.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
1548 intrusive_ptr<const Line> endLine = end.getLine();
1549 for(;;) {
1550 if (line==endLine) {
1551 c.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, static_cast<int>(end.getPositionInLine())+1);
1552 break;
1553 }
1554 line = line->getNextLineInBuffer();
1555 this->codeTextEdit->append(QString::fromStdString(line->chars));
1556 c.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor);
1557 }
1558 c.mergeCharFormat(this->format);
1559 c.clearSelection();
1560 c.setCharFormat(oldFormat);
1561 this->codeTextEdit->setTextCursor(c);
1562 }
1563 this->codeTextEdit->ensureCursorVisible();
1564 }
1565
fillCallStack()1566 void Debugger::fillCallStack() {
1567 this->listedFrames.clear();
1568 this->callStackList->clear();
1569 RuntimeEnvironment &re = this->interpreter->runtimeEnvironment;
1570 const Frame *currentFrame = re.getCurrentFrame();
1571 const Frame *tlFrame = re.getTopLevelFrame();
1572 for(;currentFrame>tlFrame;--currentFrame) {
1573 if (currentFrame->userdefinedFun) {
1574 this->listedFrames.push_back(currentFrame);
1575 string name(currentFrame->userdefinedFun->fnDecl->fnName);
1576 if (name.empty())
1577 name = "<anonymous function>";
1578 this->callStackList->addItem(QString::fromStdString("Fn-proc "+name));
1579 }
1580 }
1581 this->callStackList->addItem("Top-level");
1582 assert(!tlFrame->userdefinedFun);
1583 this->listedFrames.push_back(tlFrame);
1584 this->callStackList->setCurrentRow(0);
1585 }
1586
clearAndDisable()1587 void Debugger::clearAndDisable() {
1588 this->setEnabled(false);
1589 this->callStackList->clear();
1590 this->localsTree->clear();
1591 this->codeTextEdit->clear();
1592 }
1593
onStepIntoClicked()1594 void Debugger::onStepIntoClicked() {
1595 this->clearAndDisable();
1596 this->interpreter->resume();
1597 }
1598
onStepOverClicked()1599 void Debugger::onStepOverClicked() {
1600 this->clearAndDisable();
1601 this->interpreter->singleStepExecution = false;
1602 this->interpreter->doStepOver = true;
1603 this->interpreter->resume();
1604 }
1605
onStepOutFnProcClicked()1606 void Debugger::onStepOutFnProcClicked() {
1607 this->clearAndDisable();
1608 Frame *f = this->interpreter->runtimeEnvironment.getCurrentFunctionFrameOrNull();
1609 assert(f);
1610 if (f) {
1611 f->singleStepOnPop = true;
1612 this->interpreter->singleStepExecution = false;
1613 }
1614 this->interpreter->resume();
1615 }
1616
onStepOutClicked()1617 void Debugger::onStepOutClicked() {
1618 this->clearAndDisable();
1619 this->interpreter->runtimeEnvironment.getCurrentFrame()->singleStepOnPop = true;
1620 this->interpreter->singleStepExecution = false;
1621 this->interpreter->resume();
1622 }
1623
onContinueClicked()1624 void Debugger::onContinueClicked() {
1625 this->clearAndDisable();
1626 this->interpreter->singleStepExecution = false;
1627 this->interpreter->resume();
1628 }
1629
onStopClicked()1630 void Debugger::onStopClicked()
1631 {
1632 this->clearAndDisable();
1633 this->interpreter->singleStepExecution = false;
1634 CoCoA::SetSignalReceived(2/*SIGINT*/); // actual signal does not matter (I believe)
1635 this->interpreter->resume();
1636 }
1637
initColorSchemes()1638 void MainWindow::initColorSchemes() {
1639 static bool done;
1640 if (done)
1641 return;
1642 // Anna's color scheme
1643 csAnna.stringLiterals.foreground = QColor::fromRgb(0, 0x8b, 0);
1644 csAnna.constants.foreground = QColor::fromRgb(0xcd, 0x85, 0);
1645 csAnna.comments.foreground = QColor::fromRgb(0xcd, 0, 0);
1646 csAnna.keywords.foreground = QColor::fromRgb(0x36, 0x64, 0x8b);
1647 csAnna.types.foreground = QColor::fromRgb(0xcd, 0x69, 0xc9);
1648 csAnna.numericalLiterals.foreground = QColor::fromRgb(0, 0, 0);
1649 csAnna.unknownChars.foreground = QColor::fromRgb(0xff, 0xff, 0xff);
1650 csAnna.unknownChars.background = QColor::fromRgb(0xff, 0, 0);
1651 csAnna.braceMatch.foreground = QColor::fromRgb(0x8b, 0, 0x8b);
1652 csAnna.braceMatch.background = QColor::fromRgb(0xff, 0xff, 0);
1653 csAnna.braceMismatch.foreground = QColor::fromRgb(0xff, 0xff, 0xff);
1654 csAnna.braceMismatch.background = QColor::fromRgb(0xff, 0, 0);
1655 // Gio's color scheme
1656 csGio.stringLiterals.foreground = QColor::fromRgb(0xff, 0, 0x90);
1657 csGio.constants.foreground = QColor::fromRgb(0, 0, 0xff);
1658 csGio.comments.foreground = QColor::fromRgb(0, 0x70, 0);
1659 csGio.keywords.foreground = QColor::fromRgb(0, 0, 0xff);
1660 csGio.types.foreground = QColor::fromRgb(0x64, 0x95, 0xed);
1661 csGio.numericalLiterals.foreground = QColor::fromRgb(0, 0, 0x40);
1662 csGio.unknownChars.foreground = QColor::fromRgb(0xff, 0xff, 0xff);
1663 csGio.unknownChars.background = QColor::fromRgb(0xff, 0, 0);
1664 csGio.braceMatch.foreground = QColor::fromRgb(0x8b, 0, 0x8b);
1665 csGio.braceMatch.background = QColor::fromRgb(0xff, 0xff, 0);
1666 csGio.braceMismatch.foreground = QColor::fromRgb(0xff, 0xff, 0xff);
1667 csGio.braceMismatch.background = QColor::fromRgb(0xff, 0, 0);
1668 done = true;
1669 }
1670
setHighlighterFormats()1671 void Console::setHighlighterFormats() {
1672 const ColorScheme *const cs = this->mainWindow->getCurrentColorScheme();
1673 this->inputHL->setFormats(*cs);
1674 this->inputHL->rehighlight();
1675 this->outputHL->setFormats(*cs);
1676 this->outputHL->rehighlight();
1677 }
1678
setHighlighterFormats()1679 void MainWindow::setHighlighterFormats() {
1680 QList<QMdiSubWindow *> wList = this->mdiArea->subWindowList();
1681 for (int i = 0; i < wList.size(); ++i) {
1682 QMdiSubWindow *const win = wList.at(i);
1683 if (SourceEditor *se = dynamic_cast<SourceEditor *>(win->widget())) {
1684 QEditor * const ed = se->getEditor();
1685 this->currentColorScheme->applyTo(ed->document()->formatScheme());
1686 ed->highlight();
1687 }
1688 }
1689 this->console->setHighlighterFormats();
1690 this->debugger->setHighlighterFormats();
1691 }
1692
onAnnaCS()1693 void MainWindow::onAnnaCS() {
1694 this->currentColorScheme = &csAnna;
1695 this->setHighlighterFormats();
1696 QSettings settings;
1697 settings.setValue(SETTING_COLORSCHEME_NAME, "Anna");
1698 }
1699
onGioCS()1700 void MainWindow::onGioCS() {
1701 this->currentColorScheme = &csGio;
1702 this->setHighlighterFormats();
1703 QSettings settings;
1704 settings.setValue(SETTING_COLORSCHEME_NAME, "Gio");
1705 }
1706
1707 ColorScheme MainWindow::csAnna;
1708 ColorScheme MainWindow::csGio;
1709
MainWindow(QWidget * parent,QApplication * application,WarningSeverity warningLevel,bool warnAboutCocoa5,const vector<string> & packageList,bool fullCoCoALibError)1710 MainWindow::MainWindow(QWidget *parent, QApplication *application, WarningSeverity warningLevel, bool warnAboutCocoa5, const vector<string> & packageList, bool fullCoCoALibError) :
1711 QMainWindow(parent),
1712 application(application),
1713 mdiArea(new QMdiArea(this)),
1714 oldInterpreterStatus(static_cast<Interpreter::InterpreterStatus>(-1)),
1715 statusLabel(new QLabel(this)),
1716 menuWindowActionGroup(new QActionGroup(this)),
1717 colorSchemeActionGroup(new QActionGroup(this)),
1718 actionMenuConsole(new QAction("&Console",this)),
1719 actionMenuDebugger(new QAction("&Debugger", this)),
1720 currentColorScheme(&csAnna)
1721 {
1722 initColorSchemes();
1723 this->setupUi(this);
1724 this->setCentralWidget(this->mdiArea);
1725 this->actionMenuConsole->setIcon(QIcon(":/images/utilities-terminal.png"));
1726 this->actionMenuDebugger->setIcon(QIcon(":/images/utilities-system-monitor.png"));
1727 this->toolBar->addAction(this->actionMenuConsole);
1728 this->toolBar->addAction(this->actionMenuDebugger);
1729 QSettings settings;
1730 if (settings.value(SETTING_COLORSCHEME_NAME, "Anna")=="Gio")
1731 this->currentColorScheme = &csGio;
1732 if (settings.value(SETTING_EMACS_LIKE_KEYBINDINGS, false).toBool())
1733 this->actionEmacsLike->setChecked(true);
1734 this->menuWindowActionGroup->addAction(this->actionMenuConsole);
1735 this->menuWindowActionGroup->addAction(this->actionMenuDebugger);
1736 this->colorSchemeActionGroup->addAction(this->actionAnna);
1737 this->actionAnna->setChecked(true);
1738 connect(this->actionAnna, SIGNAL(triggered()), this, SLOT(onAnnaCS()));
1739 this->colorSchemeActionGroup->addAction(this->actionGio);
1740 connect(this->actionGio, SIGNAL(triggered()), this, SLOT(onGioCS()));
1741 this->statusBar()->addWidget(this->statusLabel);
1742 this->menuWindow->addAction(this->actionMenuConsole);
1743 this->menuWindow->addAction(this->actionMenuDebugger);
1744 this->console = new Console(this, this, warningLevel, warnAboutCocoa5, packageList, fullCoCoALibError);
1745 this->debugger = new Debugger(this, this);
1746 this->debuggerMdiSubWin = this->mdiArea->addSubWindow(this->debugger);
1747 this->debuggerMdiSubWin->setWindowIcon(this->debugger->windowIcon());
1748 this->debuggerMdiSubWin->showMinimized();
1749 this->actionMenuDebugger->setCheckable(true);
1750 connect(this->actionMenuDebugger, SIGNAL(triggered()), this->debuggerMdiSubWin, SLOT(setFocus()));
1751 this->interpreter = this->debugger->interpreter = this->console->interpreter.get();
1752 this->consoleMdiSubWin = this->mdiArea->addSubWindow(this->console);
1753 this->consoleMdiSubWin->setWindowIcon(this->console->windowIcon());
1754 this->actionMenuConsole->setCheckable(true);
1755 connect(this->actionMenuConsole, SIGNAL(triggered()), this->console->inputTextEdit, SLOT(setFocus()));
1756 this->consoleMdiSubWin->show();
1757 connect(this->actionNew, SIGNAL(triggered()), this, SLOT(onFileNew()));
1758 connect(this->actionOpen, SIGNAL(triggered()), this, SLOT(onFileOpen()));
1759 connect(this->actionExit, SIGNAL(triggered()), this, SLOT(onFileExit()));
1760 connect(this->actionClose, SIGNAL(triggered()), this, SLOT(onWindowClose()));
1761 connect(this->actionClose_All, SIGNAL(triggered()), this, SLOT(onWindowCloseAll()));
1762 connect(this->actionTile, SIGNAL(triggered()), this, SLOT(onWindowTile()));
1763 connect(this->actionCascade, SIGNAL(triggered()), this, SLOT(onWindowCascade()));
1764 connect(this->actionNext, SIGNAL(triggered()), this, SLOT(onWindowNext()));
1765 connect(this->actionPrevious, SIGNAL(triggered()), this, SLOT(onWindowPrevious()));
1766 connect(this->actionAbout, SIGNAL(triggered()), this, SLOT(onHelpAbout()));
1767 connect(this->actionFont, SIGNAL(triggered()), this, SLOT(onOptionsFont()));
1768 connect(this->actionEmacsLike, SIGNAL(triggered()), this, SLOT(onOptionsEmacsLike()));
1769 connect(this->actionFileSaveOutputWindow, SIGNAL(triggered()), this, SLOT(onFileSaveOutputWindow()));
1770 connect(this->mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(onSubWindowActivated(QMdiSubWindow*)));
1771 }
1772
onOptionsFont()1773 void MainWindow::onOptionsFont() {
1774 bool ok;
1775 QFont font = QFontDialog::getFont(&ok, this);
1776 if (ok) {
1777 QDocument::setFont(font);
1778 this->application->setFont(font);
1779 QSettings settings;
1780 settings.setValue(SETTING_FONT_FAMILY, font.family());
1781 settings.setValue(SETTING_FONT_POINTSIZE, font.pointSize());
1782 settings.setValue(SETTING_FONT_WEIGHT, font.weight());
1783 settings.setValue(SETTING_FONT_ITALIC, font.italic());
1784 }
1785 }
1786
onOptionsEmacsLike()1787 void MainWindow::onOptionsEmacsLike() {
1788 QSettings settings;
1789 settings.setValue(SETTING_EMACS_LIKE_KEYBINDINGS, this->actionEmacsLike->isChecked());
1790 if (this->mdiArea->subWindowList().size()!=2 /* that is, console and debugger */)
1791 QMessageBox::information(this, "Warning", "Note: this choice affects only newly-opened editors, already opened editors keep their key-bindings.");
1792 }
1793
1794 intrusive_ptr<IncrementProgressBarQC> IncrementProgressBarQC::theInstance(new IncrementProgressBarQC);
1795
execute(Console * console)1796 void IncrementProgressBarQC::execute(Console *console) {
1797 QProgressBar *bar = console->packageLoadingProgressBar;
1798 const int newValue = bar->value()+1;
1799 bar->setValue(newValue);
1800 if (newValue==bar->maximum()) {
1801 console->bottomLeftVL->removeWidget(console->packageLoadingProgressBar);
1802 console->inputTextEdit->setEnabled(true);
1803 console->enterButton->setEnabled(true);
1804 delete console->packageLoadingProgressBar;
1805 console->packageLoadingProgressBar = 0;
1806 console->inputTextEdit->setFocus();
1807 }
1808 }
1809
myLoadPackages()1810 bool Console::myLoadPackages() {
1811 bool result = true;
1812 this->outputStream->print("", false, IdeOutputStream::normalFormat); // disable the HL for the following lines
1813 if (this->packageList.empty())
1814 {
1815 string LINE; LINE.resize(47, '-'); // 47 because it looks right on my screen
1816 this->outputStream->print(LINE)->newline();
1817 this->outputStream->print(PACKAGES_NOT_FOUND)->newline();
1818 this->outputStream->print(LINE)->newline();
1819 }
1820 BOOST_FOREACH(const string &fullname, this->packageList) {
1821 this->postCommand(IncrementProgressBarQC::theInstance);
1822 if (result)
1823 {
1824 // 2015-07-28 JAA this->outputStream->print(PACKAGE_AUTOLOAD_LOADING, false, IdeOutputStream::normalFormat);
1825 // 2015-07-28 JAA this->outputStream->print(fullname)->newline();
1826 interpreter->readAndExecute(fullname, true, true);
1827 if (interpreter->errorReporter->getErrorCount())
1828 {
1829 // JAA 2015-07-29 this->outputStream->print(PACKAGE_AUTOLOAD_FAILURE_MESSAGE)->flush();
1830 result = false;
1831 }
1832 }
1833 // 2015-07-28 suppress printing of "skipping package..."
1834 // else
1835 // {
1836 // this->outputStream->print(PACKAGE_AUTOLOAD_SKIPPING_PKG_DUE_TO_FAILURE, false, IdeOutputStream::normalFormat);
1837 // this->outputStream->print(fullname)->newline();
1838 // }
1839 }
1840 this->outputStream->print(CoCoA5BannerNonFixedWidthFonts())->newline();
1841 if (!result)
1842 this->outputStream->print(PACKAGE_AUTOLOAD_FAILURE_MESSAGE)->flush();
1843 this->postCommand(IncrementProgressBarQC::theInstance);
1844 return result;
1845 }
1846
onSubWindowActivated(QMdiSubWindow * activatedWindow)1847 void MainWindow::onSubWindowActivated(QMdiSubWindow *activatedWindow) {
1848 //cout << "activatedWindow " << static_cast<void *>(activatedWindow) << endl;
1849 const bool thereIsWin = activatedWindow!=0;
1850 this->actionClose->setEnabled(thereIsWin && activatedWindow!=this->consoleMdiSubWin && activatedWindow!=this->debuggerMdiSubWin);
1851 if (thereIsWin) {
1852 if (activatedWindow==this->consoleMdiSubWin) {
1853 this->actionMenuConsole->setChecked(true);
1854 this->console->inputTextEdit->setFocus();
1855 }
1856 else if (activatedWindow==this->debuggerMdiSubWin)
1857 this->actionMenuDebugger->setChecked(true);
1858 else if (SourceEditor *ed = dynamic_cast<SourceEditor *>(activatedWindow->widget())) {
1859 ed->menuAction->setChecked(true);
1860 ed->getEditor()->setFocus();
1861 } else {
1862 assert(false); // what's going on?!?
1863 }
1864 }
1865 }
1866
updateStatusLabel()1867 void MainWindow::updateStatusLabel() {
1868 assert(this_thread::get_id()==Console::guiThreadId);
1869 const Interpreter::InterpreterStatus status = this->console->interpreter->getStatus();
1870 if (status==this->oldInterpreterStatus)
1871 return;
1872 QString message;
1873 bool buttonsEnabled = false;
1874 switch ((this->oldInterpreterStatus = status)) {
1875 case Interpreter::IS_WAITING_FOR_COMMAND:
1876 this->interpreter->singleStepExecution = false;
1877 if (this->consoleMdiSubWin->isMinimized())
1878 this->consoleMdiSubWin->showNormal();
1879 this->console->inputTextEdit->setFocus();
1880 message = "The interpreter is <b>waiting</b> for a command";
1881 break;
1882 case Interpreter::IS_WAITING_FOR_COMMAND_COMPLETION:
1883 message = "The interpreter is <b>waiting</b> for the ending of the current command";
1884 break;
1885 case Interpreter::IS_RUNNING:
1886 message = "The interpreter is <b>running</b>";
1887 buttonsEnabled = true;
1888 break;
1889 case Interpreter::IS_RUNNING_BUILTIN:
1890 message = "The interpreter is <b>running</b> a built-in function";
1891 buttonsEnabled = true;
1892 break;
1893 case Interpreter::IS_PAUSED:
1894 message = "The interpreter is <b>paused</b>";
1895 if (this->debuggerMdiSubWin->isMinimized())
1896 this->debuggerMdiSubWin->showNormal();
1897 this->debuggerMdiSubWin->setFocus();
1898 break;
1899 case Interpreter::IS_ENDED:
1900 message = "The interpreter has <b>quit</b>";
1901 this->onFileExit();
1902 break;
1903 }
1904 this->console->pauseButton->setEnabled(buttonsEnabled);
1905 this->console->interruptButton->setEnabled(buttonsEnabled);
1906 this->statusLabel->setText(message);
1907 this->debugger->update();
1908 }
1909
onPauseClicked()1910 void Console::onPauseClicked() {
1911 this->interpreter->singleStepExecution = true;
1912 }
1913
onInterruptClicked()1914 void Console::onInterruptClicked()
1915 {
1916 if (QMessageBox::question(
1917 this,
1918 tr("Interrupt confirmation"),
1919 tr("Are you sure you want to abort the current computation?\n\nNote: built-in fn-procs cannot be interrupted, so it might take a while before the computation actually halts"),
1920 QMessageBox::Yes|QMessageBox::No,
1921 QMessageBox::No)==QMessageBox::Yes)
1922 CoCoA::SetSignalReceived(2/*SIGINT*/); // actual signal does not matter (I believe)
1923 }
1924
onFileOpen()1925 void MainWindow::onFileOpen() {
1926 assert(this_thread::get_id()==Console::guiThreadId);
1927 QString fn = QFileDialog::getOpenFileName(this, "Open CoCoA 5 source...", "", tr("Sources (*.cocoa5 *.cpkg5)"));
1928 if (!fn.size())
1929 return;
1930 SourceEditor *sourceEditor = this->editorFor(fn, true);
1931 if (!sourceEditor) {
1932 sourceEditor = this->onFileNew();
1933 sourceEditor->load(fn);
1934 }
1935 }
1936
onFileExit()1937 void MainWindow::onFileExit() {
1938 assert(this_thread::get_id()==Console::guiThreadId);
1939 if (this->close())
1940 this->application->quit();
1941 }
1942
onWindowClose()1943 void MainWindow::onWindowClose() {
1944 assert(this_thread::get_id()==Console::guiThreadId);
1945 QMdiSubWindow *w = this->mdiArea->activeSubWindow();
1946 if (w)
1947 w->close();
1948 }
1949
onWindowCloseAll()1950 void MainWindow::onWindowCloseAll() {
1951 assert(this_thread::get_id()==Console::guiThreadId);
1952 this->mdiArea->closeAllSubWindows();
1953 }
1954
onWindowTile()1955 void MainWindow::onWindowTile() {
1956 assert(this_thread::get_id()==Console::guiThreadId);
1957 this->mdiArea->tileSubWindows();
1958 }
1959
onWindowCascade()1960 void MainWindow::onWindowCascade() {
1961 assert(this_thread::get_id()==Console::guiThreadId);
1962 this->console->lower();
1963 this->mdiArea->cascadeSubWindows();
1964 }
1965
onWindowNext()1966 void MainWindow::onWindowNext() {
1967 assert(this_thread::get_id()==Console::guiThreadId);
1968 this->mdiArea->activateNextSubWindow();
1969 }
1970
onWindowPrevious()1971 void MainWindow::onWindowPrevious() {
1972 assert(this_thread::get_id()==Console::guiThreadId);
1973 this->mdiArea->activatePreviousSubWindow();
1974 }
1975
onHelpAbout()1976 void MainWindow::onHelpAbout() {
1977 assert(this_thread::get_id()==Console::guiThreadId);
1978 QMessageBox::about(this, "About C5",
1979 QString::fromStdString("<H1 align=center>C5</H1>"
1980 "<H3 align=center>CoCoA 5 Integrated Development Environment</H3>"
1981 "<p><p align=center>Alpha-version, for testing purposes only."
1982 "<p align=center>Please, <font color=red><strong>DO NOT DISTRIBUTE</strong></font> this file."
1983 "<p><p><p>For more information about CoCoA, please visit the official <a href=\"http://cocoa.dima.unige.it/\">website</a>"));
1984 }
1985
1986 namespace {
loadSettings(QApplication & app)1987 void loadSettings(QApplication &app) {
1988 QSettings settings;
1989 QVariant fontFamily, fontPointSize, fontWeight, fontItalic;
1990 fontFamily = settings.value(SETTING_FONT_FAMILY);
1991 if (!fontFamily.isNull()) {
1992 fontPointSize = settings.value(SETTING_FONT_POINTSIZE);
1993 fontWeight = settings.value(SETTING_FONT_WEIGHT);
1994 fontItalic = settings.value(SETTING_FONT_ITALIC);
1995 //cout << "setting font " << fontFamily.toString().toStdString() << ", point-size=" << fontPointSize.toInt()
1996 // << ", weight=" << fontWeight.toInt() << ", italic=" << fontItalic.toBool() << endl;
1997 app.setFont(QFont(fontFamily.toString(), fontPointSize.toInt(), fontWeight.toInt(), fontItalic.toBool()));
1998 }
1999 }
2000 }
2001
launchTheIDE(WarningSeverity warningLevel,bool warnAboutCocoa5,const vector<string> & packageList,bool fullCoCoALibError)2002 int launchTheIDE(WarningSeverity warningLevel, bool warnAboutCocoa5, const vector<string> &packageList, bool fullCoCoALibError) {
2003 QCoreApplication::setOrganizationName("CoCoA Team");
2004 QCoreApplication::setOrganizationDomain("cocoa.dima.unige.it");
2005 QCoreApplication::setApplicationName("C5");
2006 char *argv[] = {const_cast<char *>("C5"), 0};
2007 int argc = 1;
2008 QApplication::setStyle(new QPlastiqueStyle);
2009 QApplication app(argc, argv);
2010 initQCodeEdit();
2011 app.setWindowIcon(QIcon(":/images/CoCoALogo-icon.png"));
2012 loadSettings(app);
2013 QEditor::setDefaultFlags(QEditor::defaultFlags()|QEditor::LineWrap);
2014 QDocument::setFont(app.font());
2015 CoCoA::IDE::MainWindow main(0, &app, warningLevel, warnAboutCocoa5, packageList, fullCoCoALibError);
2016 main.showNormal();
2017 return app.exec();
2018 }
2019
loadPackages(Console * console)2020 bool loadPackages(Console *console) {
2021 return console->myLoadPackages();
2022 }
2023
2024 } // namespace IDE
2025
2026 } // namespace CoCoA
2027
2028 #endif // #ifdef C5IDE
2029