diff --git a/src/canvas.cpp b/src/canvas.cpp index aeebb6d..8f466f2 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -1,287 +1,287 @@ /* Copyright (C) 2003-2006 Cies Breijs This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "canvas.h" #include #include #include #include // this function is used in executer and canvas: #define ROUND2INT(x) ( (x) >= 0 ? (int)( (x) + .5 ) : (int)( (x) - .5 ) ) #ifndef M_PI #define M_PI 3.14159265358979323846264338327950288419717 #endif const double DegToRad = M_PI / 180.0; int kTurtleZValue = 1; int kCanvasFrameZValue = -10000; int kCanvasMargin = 20; Canvas::Canvas(QWidget *parent) : QGraphicsView(parent) { // create a new scene for this view _scene = new QGraphicsScene(parent); //_scene->setItemIndexMethod(QGraphicsScene::NoIndex); //_scene->setSceneRect(-200, -200, 400, 400); // (-50, -50, 50, 50); setCacheMode(CacheBackground); setRenderHint(QPainter::Antialiasing); setTransformationAnchor(AnchorUnderMouse); setResizeAnchor(AnchorViewCenter); setMinimumSize(100, 100); // foreground pen for drawing pen = new QPen(); // font textFont = new QFont(); // Canvas area marker canvasFrame = new QGraphicsRectItem(); canvasFrame->setZValue(kCanvasFrameZValue); _scene->addItem(canvasFrame); // the turtle shape turtle = new Sprite(); turtle->setZValue(kTurtleZValue); // above the others _scene->addItem(turtle); // set initial values initValues(); setInteractive(false); // at last we assign the scene to the view setScene(_scene); } Canvas::~Canvas() { delete pen; delete turtle; delete canvasFrame; delete textFont; delete _scene; } void Canvas::initValues() { _scene->setSceneRect(QRectF(0, 0, 400, 400)); canvasFrame->setBrush(QBrush()); canvasFrame->setRect(_scene->sceneRect()); fitInView(_scene->sceneRect().adjusted(kCanvasMargin * -1, kCanvasMargin * -1, kCanvasMargin, kCanvasMargin), Qt::KeepAspectRatio); turtle->setPos(200, 200); turtle->setAngle(0); _scene->setBackgroundBrush(QBrush(Qt::white)); pen->setColor(Qt::black); pen->setWidth(1); penWidthIsZero = false; textColor.setRgb(0, 0, 0) ; delete textFont; textFont = new QFont(); // Reset our pen to the default position slotPenDown(); // Show turtle, might have been hidden in the last run slotSpriteShow(); } void Canvas::resizeEvent(QResizeEvent* event) { fitInView(_scene->sceneRect().adjusted(kCanvasMargin*-1,kCanvasMargin*-1,kCanvasMargin,kCanvasMargin), Qt::KeepAspectRatio); event->accept(); } QColor Canvas::rgbDoublesToColor(double r, double g, double b) { return QColor(qMin(qMax((int)r, 0), 255), qMin(qMax((int)g, 0), 255), qMin(qMax((int)b, 0), 255)); } void Canvas::drawLine(double x1, double y1, double x2, double y2) { if (penWidthIsZero) return; - QGraphicsLineItem* line = new QGraphicsLineItem(QLineF(x1, y1, x2, y2), 0); + QGraphicsLineItem* line = new QGraphicsLineItem(QLineF(x1, y1, x2, y2), nullptr); _scene->addItem(line); line->setPen(*pen); lines.append(line); } void Canvas::slotClear() { QList list = _scene->items(); foreach (QGraphicsItem* item, list) { // delete all but the turtle (who lives on a separate layer with z-value 1) if ((item->zValue() != kTurtleZValue) && (item->zValue() != kCanvasFrameZValue)) delete item; } } void Canvas::slotForward(double x) { double x2 = turtle->pos().x() + (x * std::sin(turtle->angle() * DegToRad)); double y2 = turtle->pos().y() - (x * std::cos(turtle->angle() * DegToRad)); drawLine(turtle->pos().x(), turtle->pos().y(), x2, y2); slotGo(x2, y2); } void Canvas::slotBackward(double x) { double x2 = turtle->pos().x() - ( x * std::sin(turtle->angle() * DegToRad) ); double y2 = turtle->pos().y() + ( x * std::cos(turtle->angle() * DegToRad) ); drawLine(turtle->pos().x(), turtle->pos().y(), x2, y2); slotGo(x2, y2); } void Canvas::slotCenter() { slotGo(_scene->width()/2, _scene->height()/2); } void Canvas::slotPenWidth(double width) { int w = qMax(ROUND2INT(width), 0); if (w == 0) { penWidthIsZero = true; return; } else { penWidthIsZero = false; if (w == 1) pen->setWidth(0); else pen->setWidthF(width); } } void Canvas::slotPenColor(double r, double g, double b) { pen->setColor(rgbDoublesToColor(r, g, b)); textColor.setRgb((int)r, (int)g, (int)b); } void Canvas::slotCanvasColor(double r, double g, double b) { //_scene->setBackgroundBrush(QBrush(rgbDoublesToColor(r, g, b))); canvasFrame->setBrush(QBrush(rgbDoublesToColor(r, g, b))); } void Canvas::slotCanvasSize(double r, double g) { _scene->setSceneRect(QRectF(0,0,r,g)); canvasFrame->setRect(_scene->sceneRect()); fitInView(_scene->sceneRect(), Qt::KeepAspectRatio); } void Canvas::slotPrint(const QString& text) { QGraphicsTextItem *ti = new QGraphicsTextItem(text, nullptr); _scene->addItem(ti); // ti->setDefaultTextColor(textColor); ti->setFont(*textFont); ti->setTransform(QTransform().rotate(turtle->angle()), true); ti->setPos(turtle->pos().x(), turtle->pos().y()); ti->setDefaultTextColor(textColor); } void Canvas::slotFontType(const QString& family, const QString& extra) { textFont->setFamily(family); textFont->setBold(extra.contains(i18n("bold"))); textFont->setItalic(extra.contains(i18n("italic"))); textFont->setUnderline(extra.contains(i18n("underline"))); textFont->setOverline(extra.contains(i18n("overline"))); textFont->setStrikeOut(extra.contains(i18n("strikeout"))); } void Canvas::slotReset() { slotClear(); initValues(); } void Canvas::wheelEvent(QWheelEvent *event) { scaleView(std::pow((double)2.0, -event->delta() / 240.0)); } void Canvas::scaleView(double scaleFactor) { qreal factor = matrix().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width(); if (factor < 0.07 || factor > 100) return; scale(scaleFactor, scaleFactor); } void Canvas::getX(double& value) { value = turtle->pos().x(); } void Canvas::getY(double& value) { value = turtle->pos().y(); } void Canvas::getDirection(double &value) { value = fmod(turtle->angle(), 360); } QImage Canvas::getPicture() { QImage png(sceneRect().size().toSize(), QImage::Format_RGB32); // create a painter to draw on the image QPainter p(&png); p.setRenderHint(QPainter::Antialiasing); // antialiasing like our Canvas _scene->render(&p); p.end(); return png; } void Canvas::saveAsSvg(const QString& title, const QString& fileName) { Q_UNUSED(title); // it would have been nicer if this method didn't needed to be passed a filename.. // but otherwise some QBuffer, QByteArray, etc. thing had to be set up. QSvgGenerator generator; generator.setFileName(fileName); generator.setSize(_scene->sceneRect().size().toSize()); generator.setViewBox(_scene->sceneRect()); generator.setTitle(title); // generator.setDescription(i18n("Created with KTurtle %1 -- %2").arg(version).arg(website)); // create a painter to draw on the image QPainter p(&generator); // p.setRenderHint(QPainter::Antialiasing); // antialiasing like our Canvas bool spriteWasVisible = turtle->isVisible(); slotSpriteHide(); // hide the sprite as it draws really ugly (especially when Qt < 4.5) _scene->render(&p); if(spriteWasVisible) slotSpriteShow(); p.end(); } diff --git a/src/editor.cpp b/src/editor.cpp index ad02e19..3964f10 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -1,465 +1,465 @@ /* Copyright (C) 2003-2008 Cies Breijs This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "editor.h" #include "interpreter/token.h" #include "interpreter/tokenizer.h" #include #include #include #include #include #include #include #include #include #include #include #include static const int CURSOR_WIDTH = 2; // in pixels static const int TAB_WIDTH = 2; // in character widths Editor::Editor(QWidget *parent) : QFrame(parent) { setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); setLineWidth(CURSOR_WIDTH); setCurrentUrl(); currentRow = 1; currentCol = 1; // setup the main view editor = new TextEdit(this); editor->document()->setDefaultFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); editor->setFrameStyle(QFrame::NoFrame); editor->installEventFilter(this); editor->setLineWrapMode(QTextEdit::WidgetWidth); editor->setTabStopWidth(editor->fontMetrics().width("0") * TAB_WIDTH); editor->setAcceptRichText(false); setFocusProxy(editor); connect(editor->document(), &QTextDocument::contentsChange, this, &Editor::textChanged); connect(editor->document(), &QTextDocument::modificationChanged, this, &Editor::setModified); connect(editor, &TextEdit::cursorPositionChanged, this, &Editor::updateOnCursorPositionChange); // setup the line number pane numbers = new LineNumbers(this, editor); numbers->setFont(editor->document()->defaultFont()); numbers->setWidth(1); connect(editor->document()->documentLayout(), SIGNAL(update(QRectF)), numbers, SLOT(update())); connect(editor->verticalScrollBar(), SIGNAL(valueChanged(int)), numbers, SLOT(update())); // let the line numbers and the editor coexist box = new QHBoxLayout(this); box->setSpacing(0); box->setMargin(0); box->addWidget(numbers); box->addWidget(editor); // calculate the bg color for the highlighted line QColor bgColor = this->palette().brush(this->backgroundRole()).color(); highlightedLineBackgroundColor.setHsv( LINE_HIGHLIGHT_COLOR.hue(), bgColor.saturation() + EXTRA_SATURATION, bgColor.value()); // our syntax highlighter (this does not do any markings) highlighter = new Highlighter(editor->document()); // create a find dialog fdialog = new KFindDialog(); fdialog->setSupportsRegularExpressionFind(false); fdialog->setHasSelection(false); fdialog->setHasCursor(false); // sets some more default values newFile(); tokenizer = new Tokenizer(); } Editor::~Editor() { delete highlighter; delete tokenizer; } void Editor::enable() { editor->viewport()->setEnabled(true); editor->setReadOnly(false); } void Editor::disable() { editor->viewport()->setEnabled(false); editor->setReadOnly(true); } void Editor::setContent(const QString& s) { editor->document()->setPlainText(s); editor->document()->setModified(false); } void Editor::openExample(const QString& example, const QString& exampleName) { if (newFile()) { setContent(example); editor->document()->setModified(false); setCurrentUrl(QUrl::fromLocalFile(exampleName)); } } void Editor::textChanged(int pos, int removed, int added) { Q_UNUSED(pos); if (removed == 0 && added == 0) return; // save some cpu cycles removeMarkings(); // removes the character markings if there are any int lineCount = 1; for (QTextBlock block = editor->document()->begin(); block.isValid(); block = block.next()) lineCount++; numbers->setWidth(qMax(1, 1 + (int)std::floor(std::log10((double)lineCount - 1)))); emit contentChanged(); } bool Editor::newFile() { if (maybeSave()) { editor->document()->clear(); setCurrentUrl(); return true; } return false; } bool Editor::openFile(const QUrl &_url) { QUrl url = _url; if (maybeSave()) { if (url.isEmpty()) { url = QFileDialog::getOpenFileUrl(this, i18n("Open"), QUrl(), QString("%1 (*.turtle);;%2 (*)").arg(i18n("Turtle code files")).arg(i18n("All files")) ); } if (!url.isEmpty()) { KIO::StoredTransferJob *job = KIO::storedGet(url); if (job->exec()) { QByteArray data = job->data(); QBuffer buffer(&data); if (!buffer.open(QIODevice::ReadOnly | QIODevice::Text)) { return false; // can't happen } QTextStream in(&buffer); // check for our magic identifier QString s; s = in.readLine(); if (s != KTURTLE_MAGIC_1_0) { KMessageBox::error(this, i18n("The file you try to open is not a valid KTurtle script, or is incompatible with this version of KTurtle.\nCannot open %1", url.toDisplayString(QUrl::PreferLocalFile))); return false; } QString localizedScript = Translator::instance()->localizeScript(in.readAll()); setContent(localizedScript); setCurrentUrl(url); editor->document()->setModified(false); emit fileOpened(url); return true; } else { KMessageBox::error(this, job->errorString()); return false; } } } // statusbar "Nothing opened" return false; } bool Editor::saveFile(const QUrl &targetUrl) { QUrl url(targetUrl); bool result = false; if (url.isEmpty() && currentUrl().isEmpty()) { result = saveFileAs(); } else { if (url.isEmpty()) url = currentUrl(); QTemporaryFile tmp; // only used for network export tmp.setAutoRemove(false); tmp.open(); QString filename = url.isLocalFile() ? url.toLocalFile() : tmp.fileName(); QSaveFile *savefile = new QSaveFile(filename); if (savefile->open(QIODevice::WriteOnly)) { QTextStream outputStream(savefile); // store commands in their generic @(...) notation format, to be translatable when reopened // this allows sharing of scripts written in different languages Tokenizer tokenizer; tokenizer.initialize(editor->document()->toPlainText()); const QStringList localizedLooks(Translator::instance()->allLocalizedLooks()); QString unstranslated; Token* t; bool pendingEOL = false; // to avoid writing a final EOL token while ((t = tokenizer.getToken())->type() != Token::EndOfInput) { if (pendingEOL) { unstranslated.append('\n'); pendingEOL = false; } if (localizedLooks.contains(t->look())) { QString defaultLook(Translator::instance()->defaultLook(t->look())); unstranslated.append(QString("@(%1)").arg(defaultLook)); } else { if (t->type() == Token::EndOfLine) pendingEOL = true; else unstranslated.append(t->look()); } } outputStream << KTURTLE_MAGIC_1_0 << '\n'; outputStream << unstranslated; outputStream.flush(); savefile->commit(); // check for error here? } delete savefile; if (!url.isLocalFile()) { - KIO::StoredTransferJob *job = KIO::storedPut(savefile, url, -1, 0); + KIO::StoredTransferJob *job = KIO::storedPut(savefile, url, -1, nullptr); if(job->exec()){ setCurrentUrl(url); editor->document()->setModified(false); // MainWindow will add us to the recent file list emit fileSaved(url); result = true; // fix GUI for saveAs and saveExamples. TODO: check 5 lines above } } } return result; } bool Editor::saveFileAs() { QUrl url = QFileDialog::getSaveFileUrl(this, i18n("Save As"), QUrl(), QString("%1 (*.turtle);;%2 (*)").arg(i18n("Turtle code files")).arg(i18n("All files")) ); if (url.isEmpty()) return false; bool result = saveFile(url); return result; } bool Editor::maybeSave() { if (!editor->document()->isModified()) return true; int result = KMessageBox::warningContinueCancel(this, i18n("The program you are currently working on is not saved. " "By continuing you may lose the changes you have made."), i18n("Unsaved File"), KGuiItem(i18n("&Discard Changes")), KStandardGuiItem::cancel(), i18n("&Discard Changes")); if (result == KMessageBox::Continue) return true; return false; } void Editor::setModified(bool b) { editor->document()->setModified(b); emit modificationChanged(); } // TODO: improve find to be able to search within a selection void Editor::find() { // find selection, etc if (editor->textCursor().hasSelection()) { QString selectedText = editor->textCursor().selectedText(); // If the selection is too big, then we don't want to automatically // populate the search text box with the selection text if (selectedText.length() < 30) { fdialog->setPattern(selectedText); } } if (fdialog->exec() == QDialog::Accepted && !fdialog->pattern().isEmpty()) { long kOpts = fdialog->options(); - QTextDocument::FindFlags qOpts = 0; + QTextDocument::FindFlags qOpts = nullptr; if (kOpts & KFind::CaseSensitive) { qOpts |= QTextDocument::FindCaseSensitively; } if (kOpts & KFind::FindBackwards) { qOpts |= QTextDocument::FindBackward; } if (kOpts & KFind::WholeWordsOnly) { qOpts |= QTextDocument::FindWholeWords; } editor->find(fdialog->pattern(), qOpts); } } void Editor::findNext() { if (!fdialog->pattern().isEmpty()) { long kOpts = fdialog->options(); - QTextDocument::FindFlags qOpts = 0; + QTextDocument::FindFlags qOpts = nullptr; if (kOpts & KFind::CaseSensitive) { qOpts |= QTextDocument::FindCaseSensitively; } if (kOpts & KFind::FindBackwards) { qOpts |= QTextDocument::FindBackward; } if (kOpts & KFind::WholeWordsOnly) { qOpts |= QTextDocument::FindWholeWords; } editor->find(fdialog->pattern(), qOpts); } } void Editor::findPrev() { if(!fdialog->pattern().isEmpty()) { long kOpts = fdialog->options(); - QTextDocument::FindFlags qOpts = 0; + QTextDocument::FindFlags qOpts = nullptr; if (kOpts & KFind::CaseSensitive) { qOpts |= QTextDocument::FindCaseSensitively; } // search in the opposite direction as findNext() if (!(kOpts & KFind::FindBackwards)) { qOpts |= QTextDocument::FindBackward; } if (kOpts & KFind::WholeWordsOnly) { qOpts |= QTextDocument::FindWholeWords; } editor->find(fdialog->pattern(), qOpts); } } void Editor::setCurrentUrl(const QUrl &url) { m_currentUrl = url; emit contentNameChanged(m_currentUrl.fileName()); } void Editor::setOverwriteMode(bool b) { editor->setOverwriteMode(b); editor->setCursorWidth(b ? editor->fontMetrics().width("0") : 2); } void Editor::updateOnCursorPositionChange() { // convert the absolute pos into a row/col pair, and set current line aswell QString s = editor->toPlainText(); int pos = editor->textCursor().position(); int row = 1; int last_break = -1; int next_break = 0; for (int i = 0; i < s.length(); i++) { if (s.at(i) == '\n' && i < pos) { last_break = i; row++; } else if (s.at(i) == '\n' && i >= pos) { next_break = i; break; } } if (next_break == 0) next_break = s.length(); if (currentRow != row) { currentRow = row; highlightCurrentLine(); editor->highlightCurrentLine(); } currentCol = pos - last_break; currentLine = s.mid(last_break+1, next_break-last_break-1); emit cursorPositionChanged(); } Token* Editor::currentToken() { tokenizer->initialize(currentLine); Token* token = tokenizer->getToken(); while (token->type() != Token::EndOfInput) { if (currentCol >= token->startCol() && currentCol <= token->endCol()) return token; delete token; token = tokenizer->getToken(); } delete token; - return 0; + return nullptr; } void Editor::insertPlainText(const QString& txt) { editor->textCursor().insertText(txt); } void Editor::paintEvent(QPaintEvent *event) { QRect rect = editor->currentLineRect(); rect.setWidth(this->width() - EDITOR_MARGIN); // don't draw too much rect.translate(0, EDITOR_MARGIN); // small hack to nicely align the line highlighting //QColor bgColor = this->palette().brush(this->backgroundRole()).color(); QPainter painter(this); const QBrush brush(highlightedLineBackgroundColor); painter.fillRect(rect, brush); painter.end(); QFrame::paintEvent(event); } QString Editor::toHtml(const QString& title, const QString& lang) { Tokenizer* tokenizer = new Tokenizer(); tokenizer->initialize(editor->document()->toPlainText()); QString html = QString(); QTextCharFormat* format; Token* token = tokenizer->getToken(); while (token->type() != Token::EndOfInput) { QString escaped; switch (token->type()) { case Token::EndOfLine: escaped = "
"; break; case Token::WhiteSpace: escaped = ""; for (int n = 0; n < token->look().length(); n++) { escaped += " "; } break; default: escaped = token->look().toHtmlEscaped(); break; } format = highlighter->tokenToFormat(token); - if (format != 0) { + if (format) { bool bold = format->fontWeight() > 50; html += QString("%3") .arg(format->foreground().color().name()) .arg(bold ? " font-weight: bold;" : "") .arg(escaped); } else { html += escaped; } token = tokenizer->getToken(); } delete tokenizer; return QString("" "" "%2" "%3").arg(lang).arg(title).arg(html); } // bool Editor::eventFilter(QObject *obj, QEvent *event) // { // if (obj != editor) return QFrame::eventFilter(obj, event); // // if (event->type() == QEvent::ToolTip) { // QHelpEvent *helpEvent = static_cast(event); // // QTextCursor cursor = editor->cursorForPosition(helpEvent->pos()); // cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor); // cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); // // QString word = cursor.selectedText(); // emit mouseHover(word); // emit mouseHover(helpEvent->pos(), word); // // // QToolTip::showText(helpEvent->globalPos(), word); // For testing // } // // return false; // } diff --git a/src/errordialog.cpp b/src/errordialog.cpp index 132ce9e..736eaec 100644 --- a/src/errordialog.cpp +++ b/src/errordialog.cpp @@ -1,163 +1,163 @@ /* Copyright (C) 2003-2008 Cies Breijs This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "errordialog.h" #include #include #include #include #include #include #include #include #include #include ErrorDialog::ErrorDialog(QWidget* parent) : QDialog(parent) { - errorList = 0; + errorList = nullptr; setWindowTitle(i18n("Errors")); setModal(false); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); QWidget *mainWidget = new QWidget(this); mainLayout->addWidget(mainWidget); QWidget *baseWidget = new QWidget(this); mainLayout->addWidget(baseWidget); baseLayout = new QVBoxLayout(baseWidget); mainLayout->addWidget(baseWidget); label = new QLabel(baseWidget); mainLayout->addWidget(label); label->setText(i18n("In this list you find the error(s) that resulted from running your code.\nGood luck!")); // \nYou can select an error and click the 'Help on Error' button for help. label->setScaledContents(true); baseLayout->addWidget(label); spacer = new QSpacerItem(10, 10, QSizePolicy::Minimum, QSizePolicy::Fixed); baseLayout->addItem(spacer); errorTable = new QTableWidget(baseWidget); mainLayout->addWidget(errorTable); errorTable->setSelectionMode(QAbstractItemView::SingleSelection); errorTable->setSelectionBehavior(QAbstractItemView::SelectRows); errorTable->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); errorTable->setShowGrid(false); errorTable->setColumnCount(3); QStringList horizontalHeaderTexts; horizontalHeaderTexts << i18n("line") << i18n("description") << i18n("code"); errorTable->setHorizontalHeaderLabels(horizontalHeaderTexts); errorTable->setEditTriggers(QAbstractItemView::NoEditTriggers); baseLayout->addWidget(errorTable); m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Help); QPushButton *user1Button = new QPushButton; m_buttonBox->addButton(user1Button, QDialogButtonBox::ActionRole); connect(m_buttonBox, &QDialogButtonBox::accepted, this, &ErrorDialog::accept); connect(m_buttonBox, &QDialogButtonBox::rejected, this, &ErrorDialog::reject); connect(m_buttonBox, &QDialogButtonBox::helpRequested, this, &ErrorDialog::helpRequested); mainLayout->addWidget(m_buttonBox); KGuiItem::assign(user1Button, KGuiItem(i18n("Hide Errors"))); // setButtonGuiItem(User1, i18n("Help on &Error")); // TODO context help in the error dialog user1Button->setDefault(true); clear(); } void ErrorDialog::clear() { disable(); - errorList = 0; + errorList = nullptr; errorTable->clearContents(); // put a friendly 'nothing to see here' notice in the empty table errorTable->setRowCount(1); QTableWidgetItem* emptyItem = new QTableWidgetItem(i18n("No errors occurred yet.")); QFont emptyFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); emptyFont.setItalic(true); emptyItem->setFont(emptyFont); errorTable->setItem(0, 1, emptyItem); errorTable->resizeColumnsToContents(); // errorTable->setColumnWidth(0, errorTable->fontMetrics().width("88888")); // errorTable->setColumnWidth(2, errorTable->fontMetrics().width("88888")); // errorTable->setColumnWidth(1, errorTable->width() - errorTable->verticalHeader()->width() - errorTable->columnWidth(0) - errorTable->columnWidth(2)); } void ErrorDialog::enable() { - Q_ASSERT (errorList != 0); + Q_ASSERT (errorList); errorTable->setEnabled(true); m_buttonBox->button(QDialogButtonBox::Help)->setEnabled(true); connect(errorTable, &QTableWidget::itemSelectionChanged, this, &ErrorDialog::selectedErrorChangedProxy); errorTable->selectRow(0); } void ErrorDialog::disable() { disconnect(errorTable, &QTableWidget::itemSelectionChanged, this, &ErrorDialog::selectedErrorChangedProxy); errorTable->setEnabled(false); m_buttonBox->button(QDialogButtonBox::Help)->setEnabled(false); errorTable->clearSelection(); } void ErrorDialog::setErrorList(ErrorList *list) { errorList = list; errorTable->setRowCount(errorList->size()); int row = 0; foreach (const ErrorMessage &error, *errorList) { int col = 0; QStringList itemTexts; itemTexts << QString::number(error.token().startRow()) << error.text() << QString::number(error.code()); foreach (const QString &itemText, itemTexts) { errorTable->setItem(row, col, new QTableWidgetItem(itemText)); col++; } row++; } errorTable->clearSelection(); errorTable->resizeColumnsToContents(); enable(); } void ErrorDialog::selectedErrorChangedProxy() { - Q_ASSERT (errorList != 0); + Q_ASSERT (errorList); const Token* t = &errorList->at(errorTable->selectedItems().first()->row()).token(); emit currentlySelectedError(t->startRow(), t->startCol(), t->endRow(), t->endCol()); // //qDebug() << "EMITTED: " << t->startRow() << ", " << t->startCol() << ", " << t->endRow() << ", " << t->endCol(); } void ErrorDialog::helpRequested() { KHelpClient::invokeHelp("reference", "kturtle"); } diff --git a/src/highlighter.cpp b/src/highlighter.cpp index 7716ede..41b7792 100644 --- a/src/highlighter.cpp +++ b/src/highlighter.cpp @@ -1,121 +1,121 @@ /* Copyright (C) 2003-2006 Cies Breijs This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "highlighter.h" #include "interpreter/tokenizer.h" Highlighter::Highlighter(QTextDocument *parent) : QSyntaxHighlighter(parent) { variableFormat.setFontWeight(QFont::Bold); variableFormat.setForeground(Qt::darkMagenta); trueFalseFormat.setForeground(Qt::darkRed); numberFormat.setForeground(Qt::darkRed); commentFormat.setForeground(Qt::gray); scopeFormat.setFontWeight(QFont::Bold); scopeFormat.setForeground(Qt::darkGreen); stringFormat.setForeground(Qt::red); controllerCommandFormat.setFontWeight(QFont::Bold); controllerCommandFormat.setForeground(Qt::black); otherCommandFormat.setForeground(Qt::darkBlue); learnCommandFormat.setFontWeight(QFont::Bold); learnCommandFormat.setForeground(Qt::green); booleanOperatorFormat.setFontWeight(QFont::Bold); booleanOperatorFormat.setForeground(QColor(255, 90, 255)); // pink expressionFormat.setFontWeight(QFont::Bold); expressionFormat.setForeground(QColor(90, 100, 255)); // light blue mathOperatorFormat.setFontWeight(QFont::Bold); mathOperatorFormat.setForeground(Qt::darkGray); assignmentFormat.setFontWeight(QFont::Bold); assignmentFormat.setForeground(Qt::black); tokenizer = new Tokenizer(); } Highlighter::~Highlighter() { delete tokenizer; } QTextCharFormat* Highlighter::formatForStatement(const QString &text) { // TODO store the Token (pointer) in the respective inspector lists so we don't have to re-tokenize here... tokenizer->initialize(text); return tokenToFormat(tokenizer->getToken()); } Token* Highlighter::checkOrApplyHighlighting(const QString& text, int cursorIndex) { tokenizer->initialize(text); Token* token = tokenizer->getToken(); QTextCharFormat* format; while (token->type() != Token::EndOfInput) { format = tokenToFormat(token); - if (format != 0) { + if (format) { if (cursorIndex == -1) // does not return, but keeps running setFormat(token->startCol() - 1, token->endCol() - token->startCol(), *format); else if (cursorIndex >= token->startCol() && cursorIndex <= token->endCol()) return token; // returns, one shot only } delete token; token = tokenizer->getToken(); } delete token; - return 0; + return nullptr; } QTextCharFormat* Highlighter::tokenToFormat(Token* token) { switch (token->category()) { case Token::VariableCategory: return &variableFormat; case Token::TrueFalseCategory: return &trueFalseFormat; case Token::NumberCategory: return &numberFormat; case Token::CommentCategory: return &commentFormat; case Token::StringCategory: return &stringFormat; case Token::ScopeCategory: return &scopeFormat; case Token::CommandCategory: return &otherCommandFormat; case Token::ControllerCommandCategory: return &controllerCommandFormat; case Token::LearnCommandCategory: return &learnCommandFormat; case Token::ExpressionCategory: return &expressionFormat; case Token::BooleanOperatorCategory: return &booleanOperatorFormat; case Token::MathOperatorCategory: return &mathOperatorFormat; case Token::AssignmentCategory: return &assignmentFormat; // do nothing with these... - case Token::ParenthesisCategory: return 0; - case Token::FunctionCallCategory: return 0; - case Token::ArgumentSeparatorCategory: return 0; + case Token::ParenthesisCategory: return nullptr; + case Token::FunctionCallCategory: return nullptr; + case Token::ArgumentSeparatorCategory: return nullptr; } - return 0; + return nullptr; } diff --git a/src/inspector.cpp b/src/inspector.cpp index ec4339d..5b58aa4 100644 --- a/src/inspector.cpp +++ b/src/inspector.cpp @@ -1,319 +1,319 @@ /* Copyright (C) 2003-2008 Cies Breijs This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "inspector.h" #include "interpreter/translator.h" // for getting the translated ArgumentSeparator #include #include #include #include #include #include #include #include Inspector::Inspector(QWidget *parent) : QFrame(parent) { mainLayout = new QHBoxLayout(this); mainLayout->setSpacing(0); mainLayout->setMargin(0); tabWidget = new QTabWidget(this); variableTab = new QWidget(); variableLayout = new QHBoxLayout(variableTab); variableTable = new QTableWidget(variableTab); variableTable->setAlternatingRowColors(true); variableLayout->addWidget(variableTable); tabWidget->addTab(variableTab, i18n("Variables")); functionTab = new QWidget(); functionLayout = new QHBoxLayout(functionTab); functionTable = new QTableWidget(functionTab); functionTable->setAlternatingRowColors(true); functionLayout->addWidget(functionTable); tabWidget->addTab(functionTab, i18n("Functions")); treeTab = new QWidget(); treeLayout = new QHBoxLayout(treeTab); treeView = new QTreeWidget(treeTab); treeView->header()->setVisible(false); treeLayout->addWidget(treeView); tabWidget->addTab(treeTab, i18n("Tree")); mainLayout->addWidget(tabWidget); // our syntax highlighter (this does not do any markings) highlighter = new Highlighter(); // // the maps used when marking table/tree items later // variableMap = new QHash(); // functionMap = new QHash(); // treeMap = new QHash(); - currentlyMarkedTreeItem = 0; + currentlyMarkedTreeItem = nullptr; disable(); clear(); } Inspector::~Inspector() { delete highlighter; } void Inspector::disable() { variableTable->setEnabled(false); functionTable->setEnabled(false); treeView->setEnabled(false); // enabling happened when the inspector gets filled with data } void Inspector::clear() { clearAllMarks(); // Question: is the code duplication below enough // for a subclass-of-QTableWidget based approach? variableMap.clear(); variableTableEmpty = true; variableTable->clear(); QStringList headers; headers << i18n("name") << i18n("value") << i18n("type"); variableTable->setColumnCount(3); variableTable->setHorizontalHeaderLabels(headers); variableTable->setSelectionMode(QAbstractItemView::SingleSelection); variableTable->setSelectionBehavior(QAbstractItemView::SelectRows); variableTable->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); variableTable->verticalHeader()->setVisible(false); variableTable->setShowGrid(false); variableTable->setRowCount(1); QTableWidgetItem* emptyItem; emptyItem = new QTableWidgetItem(i18n("No variables")); variableTable->setItem(0, 0, emptyItem); variableTable->resizeColumnsToContents(); functionMap.clear(); functionTableEmpty = true; functionTable->clear(); headers.clear(); headers << i18n("name") << i18n("parameters"); functionTable->setColumnCount(2); functionTable->setHorizontalHeaderLabels(headers); functionTable->setSelectionMode(QAbstractItemView::SingleSelection); functionTable->setSelectionBehavior(QAbstractItemView::SelectRows); functionTable->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); functionTable->verticalHeader()->setVisible(false); functionTable->setShowGrid(false); functionTable->setRowCount(1); emptyItem = new QTableWidgetItem(i18n("No learned functions")); functionTable->setItem(0, 0, emptyItem); functionTable->resizeColumnsToContents(); // Treeview gets cleared in updateTree() disable(); } void Inspector::updateVariable(const QString& name, const Value& value) { // Check if the variable has already been added to the table int row = findVariable(name); if (row == -1) { // We are dealing with a new variable if (variableTableEmpty) { // Check whether we have to add a new row variableTableEmpty = false; } else { variableTable->insertRow(0); } row = 0; } QTableWidgetItem* nameItem; nameItem = new QTableWidgetItem(name); nameItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); QTextCharFormat* format = highlighter->formatForStatement(name); nameItem->setFont(format->font()); nameItem->setForeground(format->foreground()); variableTable->setItem(row, 0, nameItem); variableMap[name] = nameItem; QTableWidgetItem* valueItem; valueItem = new QTableWidgetItem(value.string()); valueItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); // TODO variable coloring when tokens are available in the inspector! // format = highlighter->formatForStatement(value.string()); // valueItem->setFont(format->font()); // valueItem->setForeground(format->foreground()); QTableWidgetItem* typeItem; switch (value.type()) { case Value::Empty: { typeItem = new QTableWidgetItem(i18nc("undefined type of a variable","empty")); QFont font = typeItem->font(); font.setItalic(true); typeItem->setFont(font); } break; case Value::Bool: typeItem = new QTableWidgetItem(i18n("boolean")); break; case Value::Number: typeItem = new QTableWidgetItem(i18n("number")); break; case Value::String: typeItem = new QTableWidgetItem(i18n("string")); break; default: // should never happen typeItem = new QTableWidgetItem("ERROR! please report to KTurtle developers"); break; } typeItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); variableTable->setItem(row, 1, valueItem); variableTable->setItem(row, 2, typeItem); variableTable->sortItems(0); variableTable->resizeColumnsToContents(); variableTable->setEnabled(true); } void Inspector::updateFunction(const QString& name, const QStringList& parameters) { // When there is already a the 'Nothing to show' line re-use that one and don't add another if (functionTableEmpty) { functionTableEmpty = false; } else { functionTable->insertRow(0); } QTableWidgetItem* nameItem = new QTableWidgetItem(name); nameItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); functionTable->setItem(0, 0, nameItem); functionMap[name] = nameItem; QTableWidgetItem* paramItem; if (parameters.empty()) { paramItem = new QTableWidgetItem(i18n("None")); QFont font = paramItem->font(); font.setItalic(true); paramItem->setFont(font); } else { QString paramList = parameters.join(Translator::instance()->default2localized(QString(","))); paramItem = new QTableWidgetItem(paramList); } paramItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); functionTable->setItem(0, 1, paramItem); functionTable->sortItems(0); functionTable->resizeColumnsToContents(); functionTable->setEnabled(true); } void Inspector::updateTree(TreeNode* rootNode) { treeMap.clear(); treeView->clear(); QTreeWidgetItem* rootItem = walkTree(rootNode); foreach (QTreeWidgetItem* item, rootItem->takeChildren()) { treeView->addTopLevelItem(item); } delete rootItem; treeView->expandAll(); treeView->setEnabled(true); } QTreeWidgetItem* Inspector::walkTree(TreeNode* node) { QTreeWidgetItem* result = new QTreeWidgetItem(); result->setText(0, node->token()->look()); QTextCharFormat* format = highlighter->tokenToFormat(node->token()); - if (format != 0) { + if (format) { result->setForeground(0, format->foreground()); QFont font(QFontDatabase::systemFont(QFontDatabase::FixedFont)); font.setBold(format->font().bold()); result->setFont(0, font); } treeMap[node] = result; if (node->hasChildren()) { for (uint i = 0; i < node->childCount(); i++) { result->addChild(walkTree(node->child(i))); } } return result; } int Inspector::findVariable(const QString& name) { QTableWidgetItem* item = variableMap[name]; - if (item == 0) return -1; + if (!item) return -1; return item->row(); // old implementation before we had a variableMap // // This function will search for a specified variable and return the row number of this variable. // QList matches = variableTable->findItems(name, Qt::MatchExactly); // QList::iterator i; // for (i = matches.begin(); i != matches.end(); ++i) { // if ((*i)->column() == 0) // only check the first column // return (*i)->row(); // } // return -1; } void Inspector::markVariable(const QString& name) { Q_UNUSED(name); //qDebug() << variableMap[name]->row(); } void Inspector::markFunction(const QString& name) { Q_UNUSED(name); //qDebug() << functionMap[name]->row(); } void Inspector::markTreeNode(TreeNode* node) { // //qDebug() << treeMap[node]->text(0); clearTreeMark(); currentlyMarkedTreeItem = treeMap[node]; previousTreeBackground = currentlyMarkedTreeItem->background(0); currentlyMarkedTreeItem->setBackground(0, QBrush(WORD_HIGHLIGHT_COLOR)); } void Inspector::clearTreeMark() { - if (currentlyMarkedTreeItem == 0) return; + if (!currentlyMarkedTreeItem) return; currentlyMarkedTreeItem->setBackground(0, previousTreeBackground); - currentlyMarkedTreeItem = 0; + currentlyMarkedTreeItem = nullptr; } void Inspector::clearAllMarks() { clearTreeMark(); } diff --git a/src/main.cpp b/src/main.cpp index 683ef88..90dd3cb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,228 +1,228 @@ /* Copyright (C) 2003-2006 Cies Breijs This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include "mainwindow.h" // for gui mode #include "interpreter/interpreter.h" // for non gui mode #include "interpreter/echoer.h" #include "interpreter/tokenizer.h" static const char description[] = I18N_NOOP("KTurtle is an educational programming environment that aims to make learning how to program as easy as possible. To achieve this KTurtle makes all programming tools available from the user interface. The programming language used is TurtleScript which allows its commands to be translated."); static const char version[] = "0.8.1 beta"; static const char copyright[] = "(c) 2003-2009 Cies Breijs"; static const char website[] = "http://edu.kde.org/kturtle"; int main(int argc, char* argv[]) { KLocalizedString::setApplicationDomain("kturtle"); QApplication app(argc, argv); app.setAttribute(Qt::AA_UseHighDpiPixmaps, true); KCrash::initialize(); /* for migration*/ QStringList configFiles; configFiles << QLatin1String("kturtlerc"); Kdelibs4ConfigMigrator migrator(QLatin1String("kturtle")); migrator.setConfigFiles(configFiles); migrator.setUiFiles(QStringList() << QStringLiteral("kturtleui.rc")); migrator.migrate(); KAboutData aboutData("kturtle", ki18n("KTurtle").toString(), ki18n(version).toString()); aboutData.setLicense(KAboutLicense::GPL); aboutData.setHomepage(ki18n(website).toString()); aboutData.setShortDescription(ki18n(description).toString()); aboutData.setCopyrightStatement(ki18n(copyright).toString()); aboutData.addAuthor(ki18n("Cies Breijs").toString(), ki18n("Initiator and core developer").toString(), "cies@kde.nl"); aboutData.addAuthor(ki18n("Niels Slot").toString(), ki18n("Core developer").toString(), "nielsslot@gmail.com"); aboutData.addAuthor(ki18n("Mauricio Piacentini").toString(), ki18n("Core developer").toString(), "piacentini@kde.org"); QCommandLineParser parser; KAboutData::setApplicationData(aboutData); parser.addVersionOption(); parser.addHelpOption(); aboutData.setupCommandLine(&parser); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("i") << QLatin1String("input"), i18n("File or URL to open (in the GUI mode)"), QLatin1String("URL or file"))); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("d") << QLatin1String("dbus"), i18n("Starts KTurtle in D-Bus mode (without a GUI), good for automated unit test scripts"))); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("t") << QLatin1String("test"), i18n("Starts KTurtle in testing mode (without a GUI), directly runs the specified local file"), QLatin1String("file"))); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("l") << QLatin1String("lang"), i18n("Specifies the localization language by a language code, defaults to \"en_US\" (only works in testing mode)"), QLatin1String("code"))); // parser.addOption(QCommandLineOption(QStringList() << QLatin1String("k") << QLatin1String("tokenize"), i18n("Only tokenizes the turtle code (only works in testing mode)"))); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("p") << QLatin1String("parse"), i18n("Translates turtle code to embeddable C++ example strings (for developers only)"), QLatin1String("file"))); parser.process(app); aboutData.processCommandLine(&parser); if (!parser.isSet("test") && !parser.isSet("parse") && !parser.isSet("dbus")) { ///////////////// run in GUI mode ///////////////// if (app.isSessionRestored()) { RESTORE(MainWindow); } else { MainWindow* mainWindow = new MainWindow(); mainWindow->show(); if (parser.isSet("input")) mainWindow->open(parser.value("input")); } // free some memory return app.exec(); // the mainwindow has WDestructiveClose flag; it will destroy itself. } else if (parser.isSet("dbus")) { ///////////////// run in DBUS mode ///////////////// Translator::instance()->setLanguage(); - new Interpreter(0, true); + new Interpreter(nullptr, true); return app.exec(); } else if (parser.isSet("parse")) { ///////////////// run in example PARSING mode ///////////////// QFile inputFile(parser.value("parse")); if (!inputFile.open(QIODevice::ReadOnly | QIODevice::Text)) { std::cout << "Could not open file: " << qPrintable(parser.value("parse")) << std::endl; std::cout << "Exiting..." << std::endl; return 1; } Translator::instance()->setLanguage(); Tokenizer tokenizer; tokenizer.initialize(inputFile.readAll()); inputFile.close(); const QStringList defaultLooks(Translator::instance()->allDefaultLooks()); QString result; Token* t; while ((t = tokenizer.getToken())->type() != Token::EndOfInput) { if (defaultLooks.contains(t->look())) result.append(QString("@(%1)").arg(t->look())); else result.append(t->look()); if (t->type() == Token::EndOfLine) result.append('\n'); } foreach (const QString &line, result.split('\n')) std::cout << qPrintable(QString("\"%1\"").arg(line)) << std::endl; std::cout << std::endl; } else { ///////////////// run without a gui ///////////////// std::cout << "KTurtle's interpreter in command line mode (version " << version << ")" << std::endl; std::cout << copyright << std::endl << std::endl; QString fileString = parser.value("test"); QFile inputFile(fileString); if (!inputFile.open(QIODevice::ReadOnly | QIODevice::Text)) { std::cout << "Could not open input file: " << qPrintable(parser.value("test")) << std::endl; std::cout << "Exitting..." << std::endl; return 1; } QTextStream in(&inputFile); // check for our magic identifier QString s; s = in.readLine(); if (s != KTURTLE_MAGIC_1_0) { std::cout << "The file you try to open is not a valid KTurtle script, or is incompatible with this version of KTurtle.\n"; return 1; } if (parser.isSet("lang")) { if (Translator::instance()->setLanguage(parser.value("lang"))) { std::cout << "Set localization to: " << parser.value("lang").data() << std::endl; } else { std::cout << "Could not set localization to:" << parser.value("lang").data() << std::endl; std::cout << "Exitting...\n"; return 1; } } else { Translator::instance()->setLanguage(); std::cout << "Using the default (en_US) localization." << std::endl; } QString localizedScript; localizedScript = Translator::instance()->localizeScript(in.readAll()); // /* if (parser.isSet("tokenize")) { // std::cout << "Tokenizing...\n" << std::endl; // QString code = inputFile.readAll(); // // for (int i = 0; i < code.length(); i++) //qDebug() << code.at(i); // Tokenizer tokenizer; // tokenizer.initialize(code); // Token* t; // while ((t = tokenizer.getToken())->type() != Token::EndOfInput) { // std::cout << "TOK> " // << qPrintable(QString("\"%1\"").arg(t->look()).leftJustified(15)) // << qPrintable(QString("[%1]").arg(QString::number(t->type())).rightJustified(5)) // << qPrintable(QString(" @ (%1,%2)").arg(t->startRow()).arg(t->startCol())) // << qPrintable(QString(" - (%1,%2)").arg(t->endRow()).arg(t->endCol())) // << std::endl; // } // return 0; // }*/ // free some memory // init the interpreter - Interpreter* interpreter = new Interpreter(0, true); // set testing to true + Interpreter* interpreter = new Interpreter(nullptr, true); // set testing to true interpreter->initialize(localizedScript); // install the echoer (new Echoer())->connectAllSlots(interpreter->getExecuter()); // the actual execution (limited to a certain amount of iterations to break endless loops) static const int MAX_ITERATION_STEPS = 20000; int i; for (i = 0; interpreter->state() != Interpreter::Finished && interpreter->state() != Interpreter::Aborted && interpreter->getErrorList()->isEmpty() && i < MAX_ITERATION_STEPS; i++) interpreter->interpret(); if (i == MAX_ITERATION_STEPS) std::cout << "ERR> Iterated more than " << MAX_ITERATION_STEPS << " steps... Execution terminated." << std::endl; } return 0; } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index fefd21f..f5b8c6b 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,1091 +1,1091 @@ /* Copyright (C) 2003-2009 Cies Breijs This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "interpreter/errormsg.h" #include "interpreter/translator.h" static const int MARGIN_SIZE = 3; // defaultly styled margins look shitty static const char* const GHNS_TARGET = "kturtle/examples"; MainWindow::MainWindow() { setupDockWindows(); // the setup order matters setupActions(); setupCanvas(); setupInterpreter(); setupEditor(); setupStatusBar(); iterationTimer = new QTimer(this); connect(iterationTimer, &QTimer::timeout, this, &MainWindow::iterate); connect(editor, &Editor::contentChanged, inspector, &Inspector::disable); connect(editor, &Editor::contentChanged, errorDialog, &ErrorDialog::disable); connect(errorDialog, &ErrorDialog::currentlySelectedError, editor, &Editor::markCurrentError); - colorPicker = 0; + colorPicker = nullptr; statusBar()->showMessage(i18nc("@info:status the application is ready for commands", "Ready")); updateContentName(); // also sets the window caption to 'untitled' setRunSpeed(1); // full speed with highlighting abort(); // sets the run-states for the actions right setupGUI(); // after all is set up: readConfig(); updateLanguagesMenu(); currentlyRunningConsole = false; } MainWindow::~MainWindow() { delete editor; KSharedConfig::openConfig()->sync(); } void MainWindow::closeEvent(QCloseEvent *event) { if (editor->maybeSave()) { event->accept(); writeConfig(); } else { event->ignore(); } } void MainWindow::filePrintDialog() { QPrinter printer; QPrintDialog *printDialog = new QPrintDialog(&printer, this); if (printDialog->exec()) { QPainter painter; painter.begin(&printer); editor->document()->drawContents(&painter); painter.end(); } delete printDialog; } void MainWindow::canvasPrintDialog() { QPrinter printer; QPrintDialog *printDialog = new QPrintDialog(&printer, this); if (printDialog->exec()) { QPainter painter; painter.begin(&printer); canvas->scene()->render(&painter); painter.end(); } delete printDialog; } void MainWindow::showDirectionDialog() { directionDialog = new DirectionDialog(canvas->turtleAngle(), this); connect(directionDialog, &DirectionDialog::pasteText, editor, &Editor::insertPlainText); } void MainWindow::showColorPicker() { - if (colorPicker == 0) { + if (!colorPicker) { colorPicker = new ColorPicker(this); connect(colorPicker, &ColorPicker::pasteText, editor, &Editor::insertPlainText); } colorPicker->show(); } void MainWindow::contextHelp() { KHelpClient::invokeHelp(contextHelpAnchor); } /*void MainWindow::whatsThis() { QWhatsThis::enterWhatsThisMode(); }*/ void MainWindow::documentWasModified() { //TODO remove this function or make it do something // setWindowModified(textEdit->document()->isModified()); } void MainWindow::setRunSpeed(int speed) { switch (speed) { case 0: dedicatedSpeedAct->setChecked(true); break; case 1: fullSpeedAct->setChecked(true); break; case 2: slowSpeedAct->setChecked(true); break; case 3: slowerSpeedAct->setChecked(true); break; case 4: slowestSpeedAct->setChecked(true); break; case 5: stepSpeedAct->setChecked(true); break; } //TODO runOptionBox->setCurrentIndex(speed); runSpeed = speed; } void MainWindow::setupActions() { QAction * a; KActionCollection* ac = actionCollection(); // WHAT IS THIS? // Similar to a status tip, but not the same. // A status tip is displayed on hover, a whatisthis is displayed when // an item is clicked on it whatisthis mode (cies doesn't like whatis mode for every little widget) // (he thinks whatis is to give the translators even more work) // File menu actions a = actionCollection()->addAction(KStandardAction::New, "file_new", editor, SLOT(newFile())); a->setStatusTip(i18n("Create a new file")); a->setWhatsThis(i18n("New File: Create a new file")); a = actionCollection()->addAction(KStandardAction::Open, "file_open", editor, SLOT(openFile())); a->setStatusTip(i18n("Open an existing file")); a->setWhatsThis(i18n("Open File: Open an existing file")); //TODO: Is this correct? -- It doesn't seem to be working recentFilesAction = (KRecentFilesAction*)actionCollection()->addAction(KStandardAction::OpenRecent, "file_recent", editor, SLOT(openFile(QUrl))); recentFilesAction->setStatusTip(i18n("Open a recently used file")); recentFilesAction->setWhatsThis(i18n("Open Recent File: Open a recently used file")); a = new QAction(QIcon::fromTheme("get-hot-new-stuff"), i18n("Get more examples..."), this); actionCollection()->addAction("get_new_examples", a); a->setText(i18n("Get more examples...")); connect(a, &QAction::triggered, this, &MainWindow::getNewExampleDialog); a = actionCollection()->addAction(KStandardAction::Save, "file_save", editor, SLOT(saveFile())); a->setStatusTip(i18n("Save the current file to disk")); a->setWhatsThis(i18n("Save File: Save the current file to disk")); connect(editor->document(), &QTextDocument::modificationChanged, a, &QAction::setEnabled); a = actionCollection()->addAction(KStandardAction::SaveAs, "file_save_as", editor, SLOT(saveFileAs())); a->setStatusTip(i18n("Save the current file under a different name")); a->setWhatsThis(i18n("Save File As: Save the current file under a different name")); exportToHtmlAct = actionCollection()->addAction("file_export_to_html"); exportToHtmlAct->setText(i18n("Export to &HTML...")); exportToHtmlAct->setStatusTip(i18n("Export the contents of the editor as HTML")); exportToHtmlAct->setWhatsThis(i18n("Export to HTML: Export the contents of the editor as HTML")); connect(exportToHtmlAct, &QAction::triggered, this, &MainWindow::exportToHtml); a = actionCollection()->addAction(KStandardAction::Print, "file_print", this, SLOT(filePrintDialog())); a->setStatusTip(i18n("Print the code")); a->setWhatsThis(i18n("Print: Print the code")); a = actionCollection()->addAction(KStandardAction::Quit, "file_quit", this, SLOT(close())); a->setStatusTip(i18n("Quit KTurtle")); a->setWhatsThis(i18n("Quit: Quit KTurtle")); // Edit menu actions a = KStandardAction::undo(editor->view(), SLOT(undo()), ac); a->setStatusTip(i18n("Undo a change in the editor")); a->setWhatsThis(i18n("Undo: Undo a change in the editor")); a->setEnabled(false); connect(editor->view(), &QTextEdit::undoAvailable, a, &QAction::setEnabled); a = KStandardAction::redo(editor->view(), SLOT(redo()), ac); a->setStatusTip(i18n("Redo a previously undone change in the editor")); a->setWhatsThis(i18n("Redo: Redo a previously undone change in the editor")); a->setEnabled(false); connect(editor->view(), &QTextEdit::redoAvailable, a, &QAction::setEnabled); a = KStandardAction::cut(editor->view(), SLOT(cut()), ac); a->setStatusTip(i18n("Cut the selected text to the clipboard")); a->setWhatsThis(i18n("Cut: Cut the selected text to the clipboard")); a->setEnabled(false); connect(editor->view(), &QTextEdit::copyAvailable, a, &QAction::setEnabled); a = KStandardAction::copy(editor->view(), SLOT(copy()), ac); a->setStatusTip(i18n("Copy the selected text to the clipboard")); a->setWhatsThis(i18n("Copy: Copy the selected text to the clipboard")); a->setEnabled(false); connect(editor->view(), &QTextEdit::copyAvailable, a, &QAction::setEnabled); a = KStandardAction::paste(editor->view(), SLOT(paste()), ac); a->setStatusTip(i18n("Paste the clipboard's content into the current selection")); a->setWhatsThis(i18n("Paste: Paste the clipboard's content into the current selection")); a = KStandardAction::selectAll(editor->view(), SLOT(selectAll()), ac); a->setStatusTip(i18n("Select all the code in the editor")); a->setWhatsThis(i18n("Select All: Select all the code in the editor")); a->setEnabled(true); a = new QAction(i18n("Overwrite Mode"), this); actionCollection()->addAction("overwrite", a ); a->setStatusTip(i18n("Toggle between the 'insert' and 'overwrite' mode")); a->setWhatsThis(i18n("Overwrite Mode: Toggle between the 'insert' and 'overwrite' mode")); actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::Key_Insert)); a->setCheckable(true); a->setChecked(false); connect(a, &QAction::toggled, this, &MainWindow::toggleOverwriteMode); a = KStandardAction::find(editor, SLOT(find()), ac); a->setStatusTip(i18n("Search through the code in the editor")); a->setWhatsThis(i18n("Find: Search through the code in the editor")); a = KStandardAction::findNext(editor, SLOT(findNext()), ac); a->setStatusTip(i18n("Continue searching through the code in the editor")); a->setWhatsThis(i18n("Find Next: Continue searching through the code in the editor")); a = KStandardAction::findPrev(editor, SLOT(findPrev()), ac); a->setStatusTip(i18n("Continue searching backwards through the code in the editor")); a->setWhatsThis(i18n("Find Previous: Continue searching backwards through the code in the editor")); //TODO: Implement search/replace //a = KStandardAction::replace(editor, SLOT(replace()), ac); //a->setStatusTip(i18n("Search and replace")); //a->setWhatsThis(i18n("Replace: Replace text in the editor")); // Canvas menu action exportToPngAct = actionCollection()->addAction("canvas_export_to_png"); exportToPngAct->setText(i18n("Export to &Image (PNG)...")); exportToPngAct->setStatusTip(i18n("Export the current canvas to a PNG raster image")); exportToPngAct->setWhatsThis(i18n("Export to PNG: Export the current canvas to a PNG raster image")); connect(exportToPngAct, &QAction::triggered, this, &MainWindow::exportToPng); exportToSvgAct = actionCollection()->addAction("canvas_export_to_svg"); exportToSvgAct->setText(i18n("Export to &Drawing (SVG)...")); exportToSvgAct->setStatusTip(i18n("Export the current canvas to Scalable Vector Graphics")); exportToSvgAct->setWhatsThis(i18n("Export to SVG: Export the current canvas to Scalable Vector Graphics")); connect(exportToSvgAct, &QAction::triggered, this, &MainWindow::exportToSvg); printCanvasAct = new QAction(QIcon::fromTheme("document-print"), i18n("&Print Canvas..."), this); actionCollection()->addAction("canvas_print", printCanvasAct); printCanvasAct->setStatusTip(i18n("Print the canvas")); printCanvasAct->setWhatsThis(i18n("Print: Print the canvas")); connect(printCanvasAct, &QAction::triggered, this, &MainWindow::canvasPrintDialog); // Run menu actions runAct = new QAction(QIcon::fromTheme("media-playback-start"), i18n("&Run"), this); actionCollection()->addAction("run", runAct); actionCollection()->setDefaultShortcut(runAct, QKeySequence(Qt::Key_F5)); runAct->setStatusTip(i18n("Execute the program")); runAct->setWhatsThis(i18n("Run: Execute the program")); connect(runAct, &QAction::triggered, this, &MainWindow::run); pauseAct = new QAction(QIcon::fromTheme("media-playback-pause"), i18n("&Pause"), this); actionCollection()->addAction("pause", pauseAct); pauseAct->setCheckable(true); actionCollection()->setDefaultShortcut(pauseAct, QKeySequence(Qt::Key_F6)); pauseAct->setStatusTip(i18n("Pause execution")); pauseAct->setWhatsThis(i18n("Pause: Pause execution")); connect(pauseAct, &QAction::triggered, this, &MainWindow::pause); abortAct = new QAction(QIcon::fromTheme("process-stop"), i18n("&Abort"), this); actionCollection()->addAction("abort", abortAct); actionCollection()->setDefaultShortcut(abortAct, QKeySequence(Qt::Key_F7)); abortAct->setStatusTip(i18n("Stop executing program")); abortAct->setWhatsThis(i18n("Abort: Stop executing program")); connect(abortAct, &QAction::triggered, this, &MainWindow::abort); // new QAction(i18n("&Indent"), "format-indent-more", CTRL+Key_I, this, SLOT(slotIndent()), ac, "edit_indent"); // new QAction(i18n("&Unindent"), "format-indent-less", CTRL+SHIFT+Key_I, this, SLOT(slotUnIndent()), ac, "edit_unindent"); // new QAction(i18n("Cl&ean Indentation"), 0, 0, this, SLOT(slotCleanIndent()), ac, "edit_cleanIndent"); // new QAction(i18n("Co&mment"), 0, CTRL+Key_D, this, SLOT(slotComment()), ac, "edit_comment"); // new QAction(i18n("Unc&omment"), 0, CTRL+SHIFT+Key_D, this, SLOT(slotUnComment()), ac, "edit_uncomment"); // Tools menu actions a = new QAction(i18n("&Direction Chooser..."), this); actionCollection()->addAction("direction_chooser", a); a->setStatusTip(i18n("Shows the direction chooser dialog")); a->setWhatsThis(i18n("Direction Chooser: Show the direction chooser dialog")); connect(a, &QAction::triggered, this, &MainWindow::showDirectionDialog); a = new QAction(i18n("&Color Picker..."), this); actionCollection()->addAction("color_picker", a); a->setStatusTip(i18n("Shows the color picker dialog")); a->setWhatsThis(i18n("Color Picker: Show the color picker dialog")); connect(a, &QAction::triggered, this, &MainWindow::showColorPicker); // Settings menu action a = new QAction(i18n("Show &Editor"), this); actionCollection()->addAction("show_editor", a); a->setStatusTip(i18n("Show or hide the Code Editor")); a->setWhatsThis(i18n("Show Code Editor: Show or hide the Code Editor")); actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::Key_E)); a->setCheckable(true); a->setChecked(true); connect(a, &QAction::toggled, editorDock, &LocalDockWidget::setVisible); connect(editorDock, &LocalDockWidget::visibilityChanged, a, &QAction::setChecked); a = new QAction(i18n("Show &Inspector"), this); actionCollection()->addAction("show_inspector", a); a->setStatusTip(i18n("Show or hide the Inspector")); a->setWhatsThis(i18n("Show Inspector: Show or hide the Inspector")); actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::Key_I)); a->setCheckable(true); a->setChecked(true); connect(a, &QAction::toggled, inspectorDock, &LocalDockWidget::setVisible); connect(inspectorDock, &LocalDockWidget::visibilityChanged, a, &QAction::setChecked); a = new QAction(i18n("Show E&rrors"), this); actionCollection()->addAction("show_errors", a); a->setStatusTip(i18n("Show or hide the Errors tab")); a->setWhatsThis(i18n("Show Errors: Show or hide the Errors tab")); a->setCheckable(true); a->setChecked(false); connect(a, &QAction::toggled, this, &MainWindow::showErrorDialog); // a = new QAction(i18n("Show &Console"), this); // actionCollection()->addAction("show_console", a); // a->setStatusTip(i18n("Show or hide the interactive Console tab")); // a->setWhatsThis(i18n("Show Console: Show or hide the interactive Console tab")); // a->setCheckable(true); // a->setChecked(false); // connect(a, SIGNAL(toggled(bool)), consoleDock, SLOT(setVisible(bool))); // connect(consoleDock, SIGNAL(visibilityChanged(bool)), a, SLOT(setChecked(bool))); a = new QAction(i18n("Show &Line Numbers"), this); actionCollection()->addAction("line_numbers", a); a->setStatusTip(i18n("Turn the line numbers on/off in the editor")); a->setWhatsThis(i18n("Show Line Numbers: Turn the line numbers on/off in the editor")); actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::Key_F11)); a->setCheckable(true); a->setChecked(true); connect(a, &QAction::toggled, editor, &Editor::toggleLineNumbers); // Help menu actions contextHelpAct = ac->addAction("context_help"); contextHelpAct->setText(""); contextHelpAct->setIcon(QIcon::fromTheme("help-about")); actionCollection()->setDefaultShortcut(contextHelpAct, QKeySequence(Qt::Key_F2)); contextHelpAct->setStatusTip(i18n("Get help on the command under the cursor")); contextHelpAct->setWhatsThis(i18n("Context Help: Get help on the command under the cursor")); connect(contextHelpAct, &QAction::triggered, this, &MainWindow::contextHelp); updateContextHelpAction(); a = actionCollection()->addAction(KStandardAction::HelpContents, "help_contents", this, SLOT(appHelpActivated())); a->setStatusTip(i18n("Help")); a->setWhatsThis(i18n("Help: Open manual for KTurtle")); // Menuless actions console = new Console(this); console->setText(i18n("Console")); actionCollection()->setDefaultShortcut(console, QKeySequence(Qt::Key_F4)); actionCollection()->addAction("console", console); connect(console, &Console::execute, this, &MainWindow::execute); executeConsoleAct = actionCollection()->addAction("execute_console"); executeConsoleAct->setIcon(QIcon::fromTheme("go-jump-locationbar")); executeConsoleAct->setText(i18n("Execute")); connect(executeConsoleAct, &QAction::triggered, console, &Console::executeActionTriggered); executeConsoleAct->setWhatsThis(i18n("Execute: Executes the current line in the console")); // The run speed action group QActionGroup* runSpeedGroup = new QActionGroup(this); // The run action collection, this is used in the toolbar to create a dropdown menu on the run button KToolBarPopupAction* runSpeedAction = new KToolBarPopupAction(QIcon::fromTheme("media-playback-start"), i18n("&Run"), this); connect(runSpeedAction, &KToolBarPopupAction::triggered, this, &MainWindow::run); QMenu* runSpeedActionMenu = runSpeedAction->menu(); actionCollection()->addAction("run_speed", runSpeedAction); runSpeedActionMenu->setStatusTip(i18n("Execute the program, or use the drop down menu to select the run speed")); runSpeedActionMenu->setWhatsThis(i18n("Run: Execute the program, or use the drop down menu to select the run speed")); connect(runSpeedActionMenu, &QMenu::triggered, this, &MainWindow::run); dedicatedSpeedAct = new QAction(i18nc("@option:radio", "Full Speed (&no highlighting and inspector)"), this); actionCollection()->addAction("dedicated_speed", dedicatedSpeedAct); dedicatedSpeedAct->setCheckable(true); dedicatedSpeedAct->setStatusTip(i18n("Run the program at full speed, with highlighting and inspector disabled")); dedicatedSpeedAct->setWhatsThis(i18n("Full Speed: Run the program at full speed, with highlighting and inspector disabled")); connect(dedicatedSpeedAct, &QAction::triggered, this, &MainWindow::setDedicatedSpeed); runSpeedGroup->addAction(dedicatedSpeedAct); runSpeedActionMenu->addAction(dedicatedSpeedAct); fullSpeedAct = new QAction(i18nc("@option:radio", "&Full Speed"), this); actionCollection()->addAction("full_speed", fullSpeedAct); fullSpeedAct->setCheckable(true); fullSpeedAct->setChecked(true); fullSpeedAct->setStatusTip(i18n("Run the program at full speed")); fullSpeedAct->setWhatsThis(i18n("Full Speed: Run the program at full speed")); connect(fullSpeedAct, &QAction::triggered, this, &MainWindow::setFullSpeed); runSpeedGroup->addAction(fullSpeedAct); runSpeedActionMenu->addAction(fullSpeedAct); slowSpeedAct = new QAction(i18nc("@option:radio choose the slow speed", "&Slow"), this); actionCollection()->addAction("slow_speed", slowSpeedAct); slowSpeedAct->setCheckable(true); slowSpeedAct->setStatusTip(i18n("Run the program at a slow speed")); slowSpeedAct->setWhatsThis(i18n("Slow Speed: Run the program at a slow speed")); connect(slowSpeedAct, &QAction::triggered, this, &MainWindow::setSlowSpeed); runSpeedGroup->addAction(slowSpeedAct); runSpeedActionMenu->addAction(slowSpeedAct); slowerSpeedAct = new QAction(i18nc("@option:radio", "S&lower"), this); actionCollection()->addAction("slower_speed", slowerSpeedAct); slowerSpeedAct->setCheckable(true); slowerSpeedAct->setStatusTip(i18n("Run the program at a slower speed")); slowerSpeedAct->setWhatsThis(i18n("Slower Speed: Run the program at a slower speed")); connect(slowerSpeedAct, &QAction::triggered, this, &MainWindow::setSlowerSpeed); runSpeedGroup->addAction(slowerSpeedAct); runSpeedActionMenu->addAction(slowerSpeedAct); slowestSpeedAct = new QAction(i18nc("@option:radio", "Sl&owest"), this); actionCollection()->addAction("slowest_speed", slowestSpeedAct); slowestSpeedAct->setCheckable(true); slowestSpeedAct->setStatusTip(i18n("Run the program at the slowest speed")); slowestSpeedAct->setWhatsThis(i18n("Slowest Speed: Run the program at the slowest speed")); connect(slowestSpeedAct, &QAction::triggered, this, &MainWindow::setSlowestSpeed); runSpeedGroup->addAction(slowestSpeedAct); runSpeedActionMenu->addAction(slowestSpeedAct); stepSpeedAct = new QAction(i18nc("@option:radio", "S&tep-by-Step"), this); actionCollection()->addAction("step_speed", stepSpeedAct); stepSpeedAct->setCheckable(true); stepSpeedAct->setStatusTip(i18n("Run the program one step at a time")); stepSpeedAct->setWhatsThis(i18n("Step Speed: Run the program one step at a time")); connect(stepSpeedAct, &QAction::triggered, this, &MainWindow::setStepSpeed); runSpeedGroup->addAction(stepSpeedAct); runSpeedActionMenu->addAction(stepSpeedAct); } void MainWindow::setupCanvas() { // put the canvas in a layout as the central widget of the mainwindow QWidget* centralWidget = new QWidget(this); QHBoxLayout* centralLayout = new QHBoxLayout(centralWidget); centralLayout->setMargin(0); // MARGIN_SIZE); canvasTabWidget = new QTabWidget(this); canvasTab = new QWidget(); QHBoxLayout* canvasLayout = new QHBoxLayout(canvasTab); canvas = new Canvas(this); canvas->setFocusPolicy(Qt::NoFocus); canvas->setRenderHint(QPainter::Antialiasing); canvas->setWhatsThis(i18n("Canvas: This is where the turtle moves and draws when the program is running")); canvasLayout->addWidget(canvas); canvasLayout->setMargin(0); canvasTabWidget->insertTab(0, canvasTab, i18n("&Canvas")); QWidget* errorTab = new QWidget(); QHBoxLayout* errorLayout = new QHBoxLayout(errorTab); errorDialog = new ErrorDialog(this); errorLayout->addWidget(errorDialog); canvasTabWidget->insertTab(1, errorTab, i18n("E&rrors")); // a widget stach with 2 layers: 1st with only a canvas, 2nd with the canvas/error tabs stackedWidget = new QStackedWidget; stackedWidget->insertWidget(0, canvasTab); stackedWidget->insertWidget(1, canvasTabWidget); centralLayout->addWidget(stackedWidget); setCentralWidget(centralWidget); } void MainWindow::showErrorDialog(bool show) { if (show) { // show the canvas and errors in a tab widget, focussing on the errors stackedWidget->setCurrentIndex(1); canvasTabWidget->insertTab(0, canvasTab, i18n("&Canvas")); canvasTabWidget->setCurrentIndex(1); actionCollection()->action("show_errors")->setChecked(true); } else { // show the canvas only stackedWidget->insertWidget(0, canvasTab); stackedWidget->setCurrentIndex(0); actionCollection()->action("show_errors")->setChecked(false); } } void MainWindow::setupDockWindows() { editorDock = new LocalDockWidget(i18n("&Editor"), this); editorDock->setObjectName("editor"); QWidget* editorWrapWidget = new QWidget(editorDock); QHBoxLayout* editorDockLayout = new QHBoxLayout(editorWrapWidget); editorDockLayout->setMargin(MARGIN_SIZE); editorWrapWidget->setLayout(editorDockLayout); // dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); editor = new Editor(editorWrapWidget); // create this here to prevent crashes editorDockLayout->addWidget(editor); editorDock->setWidget(editorWrapWidget); // editorDock->setFeatures(QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable); addDockWidget(Qt::LeftDockWidgetArea, editorDock); editor->show(); editor->setFocus(); editor->setWhatsThis(i18n("Editor: Write your KTurtle commands here")); // Creating the debug window inspectorDock = new LocalDockWidget(i18n("&Inspector"), this); inspectorDock->setObjectName("inspector"); QWidget* inspectorWrapWidget = new QWidget(inspectorDock); QHBoxLayout* inspectorDockLayout = new QHBoxLayout(inspectorWrapWidget); inspectorDockLayout->setMargin(MARGIN_SIZE); inspectorWrapWidget->setLayout(inspectorDockLayout); inspector = new Inspector(inspectorWrapWidget); inspectorDockLayout->addWidget(inspector); inspectorDock->setWidget(inspectorWrapWidget); addDockWidget(Qt::RightDockWidgetArea, inspectorDock); inspector->setWhatsThis(i18n("Inspector: See information about variables and functions when the program runs")); } void MainWindow::setupEditor() { // editor->setTranslator(Translator::instance()); connect(editor, &Editor::modificationChanged, this, &MainWindow::updateModificationState); connect(editor, &Editor::contentNameChanged, this, &MainWindow::updateContentName); connect(editor, &Editor::fileOpened, this, &MainWindow::addToRecentFilesList); connect(editor, &Editor::fileSaved, this, &MainWindow::addToRecentFilesList); connect(editor, &Editor::cursorPositionChanged, this, &MainWindow::updateOnCursorPositionChange); } void MainWindow::setupInterpreter() { interpreter = new Interpreter(this, false); connect(interpreter, &Interpreter::finished, this, &MainWindow::abort); Executer* executer = interpreter->getExecuter(); // the code to connect the executer with the canvas is auto generated: #include "interpreter/gui_connect.inc" connect(interpreter, &Interpreter::treeUpdated, inspector, &Inspector::updateTree); toggleGuiFeedback(true); } void MainWindow::toggleGuiFeedback(bool b) { Executer* executer = interpreter->getExecuter(); if (b) { connect(executer, &Executer::currentlyExecuting, editor, &Editor::markCurrentWord); connect(executer, &Executer::currentlyExecuting, inspector, &Inspector::markTreeNode); connect(executer, &Executer::variableTableUpdated, inspector, &Inspector::updateVariable); connect(executer, &Executer::functionTableUpdated, inspector, &Inspector::updateFunction); } else { disconnect(executer, &Executer::currentlyExecuting, editor, &Editor::markCurrentWord); disconnect(executer, &Executer::currentlyExecuting, inspector, &Inspector::markTreeNode); disconnect(executer, SIGNAL(variableTableUpdated(QString,Value)), inspector, SLOT(updateVariable(QString,Value))); disconnect(executer, SIGNAL(functionTableUpdated(QString,QStringList)), inspector, SLOT(updateFunction(QString,QStringList))); editor->removeMarkings(); } } void MainWindow::setupStatusBar() { statusBarLanguageLabel = new QLabel(statusBar()); statusBar()->addPermanentWidget(statusBarLanguageLabel, 0); statusBarLanguageLabel->setAlignment(Qt::AlignRight); statusBarPositionLabel = new QLabel(statusBar()); statusBar()->addPermanentWidget(statusBarPositionLabel, 0); statusBarPositionLabel->setAlignment(Qt::AlignRight); statusBarOverwriteModeLabel = new QLabel(statusBar()); statusBar()->addPermanentWidget(statusBarOverwriteModeLabel, 0); statusBarOverwriteModeLabel->setAlignment(Qt::AlignRight); statusBarFileNameLabel = new QLabel(statusBar()); statusBar()->addPermanentWidget(statusBarFileNameLabel, 0); statusBarFileNameLabel->setAlignment(Qt::AlignRight); toggleOverwriteMode(false); updateOnCursorPositionChange(); } void MainWindow::saveNewToolbarConfig() { // this destroys our actions lists ... KXmlGuiWindow::saveNewToolbarConfig(); // ... so plug them again updateLanguagesMenu(); updateExamplesMenu(); } void MainWindow::updateLanguagesMenu() { QList languageList; QActionGroup* languageGroup = new QActionGroup(this); connect(languageGroup, &QActionGroup::triggered, this, &MainWindow::setLanguage); QAction* a; // sort the dictionaries using an algorithm found in the qt docs: QMap map; QSet dictionaries = KLocalizedString::availableApplicationTranslations(); foreach (const QString &lang_code, dictionaries) map.insert(codeToFullName(lang_code), lang_code); // populate the menu: foreach (const QString &lang_code, map) { a = new QAction(codeToFullName(lang_code), actionCollection()); a->setData(lang_code); a->setStatusTip(i18n("Switch to the %1 dictionary", codeToFullName(lang_code))); a->setCheckable(true); if (lang_code == currentLanguageCode) { a->setChecked(true); } //languageMenu->addAction(a); languageGroup->addAction(a); languageList.append(a); } unplugActionList ("languages_actionlist"); plugActionList ("languages_actionlist", languageList); } void MainWindow::updateExamplesMenu() { QAction * newExample; QString actionName; QList exampleList; QActionGroup* exampleGroup = new QActionGroup (this); foreach (const QString &exampleName, Translator::instance()->exampleNames()) { newExample = new QAction (exampleName, this); newExample->setData(exampleName); exampleGroup->addAction (newExample); connect(newExample, &QAction::triggered, this, &MainWindow::openExample); exampleList.append (newExample); } QStringList allExamples; const QStringList exampleDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, GHNS_TARGET, QStandardPaths::LocateDirectory); foreach (const QString &dir, exampleDirs) { const QStringList fileNames = QDir(dir).entryList(QStringList() << "*.turtle", QDir::Files); foreach (const QString &fileName, fileNames) { allExamples.append(dir + '/' + fileName); } } if(allExamples.size()>0) { newExample = new QAction(this); newExample->setSeparator(true); exampleGroup->addAction(newExample); exampleList.append(newExample); } foreach(const QString& exampleFilename, allExamples) { QFileInfo fileInfo(exampleFilename); newExample = new QAction (fileInfo.baseName(), this); newExample->setData(exampleFilename); exampleGroup->addAction (newExample); exampleList.append (newExample); connect(newExample, &QAction::triggered, this, &MainWindow::openDownloadedExample); } unplugActionList ("examples_actionlist"); plugActionList ("examples_actionlist", exampleList); } void MainWindow::addToRecentFilesList(const QUrl &url) { recentFilesAction->addUrl(url); } void MainWindow::openExample() { QAction* action = qobject_cast(sender()); QString exampleName = action->data().toString(); editor->openExample(Translator::instance()->example(exampleName), exampleName); } void MainWindow::openDownloadedExample() { QAction* action = qobject_cast(sender()); QString exampleFilename = action->data().toString(); editor->openFile(QUrl::fromLocalFile(exampleFilename)); } void MainWindow::toggleOverwriteMode(bool b) { statusBarOverwriteModeLabel->setText(b ? i18n(" OVR ") : i18n(" INS ")); editor->setOverwriteMode(b); } void MainWindow::updateContextHelpAction(const QString& s, const QString& anchor) { //qDebug() << QString("%1 (help anchor: %2)").arg(s).arg(anchor); contextHelpAnchor = anchor; contextHelpString = s.isEmpty() ? i18n("") : s; contextHelpAct->setText(i18n("Help on: %1", contextHelpString)); } void MainWindow::updateOnCursorPositionChange() { statusBarPositionLabel->setText(i18n(" Line: %1 Column: %2 ", editor->row(), editor->col())); Token* cursorToken = editor->currentToken(); QString desc; - if (cursorToken != 0) { + if (cursorToken) { QString look = cursorToken->look(); int cat = cursorToken->category(); delete cursorToken; - cursorToken = 0; + cursorToken = nullptr; KLocalizedString layout = ki18n("\"%1\" <%2>"); switch (cat) { // not showing the look (only the name): case Token::VariableCategory: updateContextHelpAction(i18n(""), "variable"); return; case Token::NumberCategory: updateContextHelpAction(i18n(""), "number"); return; case Token::CommentCategory: updateContextHelpAction(i18n(""), "comment"); return; case Token::StringCategory: updateContextHelpAction(i18n(""), "string"); return; // only showing the look: case Token::LearnCommandCategory: updateContextHelpAction(look, "learn"); return; case Token::TrueFalseCategory: updateContextHelpAction(look, "boolean"); return; // showing the look and the name: case Token::ScopeCategory: updateContextHelpAction(layout.subs(look).subs(i18n("scope")).toString(), "scope"); return; case Token::AssignmentCategory: updateContextHelpAction(layout.subs(look).subs(i18n("assignment")).toString(), "assignment"); return; case Token::ParenthesisCategory: updateContextHelpAction(layout.subs(look).subs(i18n("parenthesis")).toString(), "parenthesis"); return; case Token::MathOperatorCategory: updateContextHelpAction(layout.subs(look).subs(i18n("mathematical operator")).toString(), "math-operator"); return; case Token::ExpressionCategory: updateContextHelpAction(layout.subs(look).subs(i18n("expression")).toString(), "expression"); return; case Token::BooleanOperatorCategory: updateContextHelpAction(layout.subs(look).subs(i18n("boolean operator")).toString(), "boolean-operator"); return; case Token::FunctionCallCategory: updateContextHelpAction(layout.subs(look).subs(i18n("learned command")).toString(), "learned-command"); return; case Token::ArgumentSeparatorCategory: updateContextHelpAction(layout.subs(look).subs(i18n("argument separator")).toString(), "argument-separator"); return; // showing the look and the name, and linking to the help through their default look (en_US): case Token::CommandCategory: updateContextHelpAction(layout.subs(look).subs(i18n("command")).toString(), Translator::instance()->defaultLook(look)); return; case Token::ControllerCommandCategory: updateContextHelpAction(layout.subs(look).subs(i18n("controller command")).toString(), Translator::instance()->defaultLook(look)); return; } } updateContextHelpAction(); // display the 'nothing under cursor thing' } void MainWindow::setLanguage(QAction *action) { if (setCurrentLanguage(action->data().toString())) action->setChecked(true); } bool MainWindow::setCurrentLanguage(const QString &lang_code) // 2 or 5 digit code (en, en_US, nl, pt_BR) { bool result = false; //qDebug() << "MainWindow::setCurrentLanguage: " << lang_code; if (Translator::instance()->setLanguage(lang_code)) { currentLanguageCode = lang_code; statusBarLanguageLabel->setText(' ' + codeToFullName(lang_code) + ' '); updateExamplesMenu(); editor->rehighlight(); result = true; } else { KMessageBox::error(this, i18n("Could not change the language to %1.", codeToFullName(lang_code))); } return result; } QString MainWindow::codeToFullName(const QString& lang_code) { return QString(lang_code == "en_US" ? i18n("English [built in]") : i18n("%1 (%2)", QLocale(lang_code.left(2)).nativeLanguageName(), lang_code) ); } void MainWindow::run() { if (interpreter->state() == Interpreter::Uninitialized || interpreter->state() == Interpreter::Finished || interpreter->state() == Interpreter::Aborted) { // reset inspector and interpreter editor->removeMarkings(); inspector->clear(); errorDialog->clear(); showErrorDialog(false); interpreter->initialize(editor->content()); } editor->disable(); console->disable(); executeConsoleAct->setEnabled(false); toggleGuiFeedback(runSpeed != 0); // start parsing (always in full speed) iterationTimer->setSingleShot(false); iterationTimer->start(0); } QString MainWindow::execute(const QString &operation) { disconnect(interpreter, &Interpreter::finished, this, &MainWindow::abort); disconnect(interpreter, &Interpreter::treeUpdated, inspector, &Inspector::updateTree); Executer* executer = interpreter->getExecuter(); disconnect(executer, &Executer::currentlyExecuting, editor, &Editor::markCurrentWord); disconnect(executer, &Executer::currentlyExecuting, inspector, &Inspector::markTreeNode); disconnect(executer, SIGNAL(variableTableUpdated(QString,Value)), inspector, SLOT(updateVariable(QString,Value))); disconnect(executer, SIGNAL(functionTableUpdated(QString,QStringList)), inspector, SLOT(updateFunction(QString,QStringList))); if (interpreter->state() == Interpreter::Uninitialized || interpreter->state() == Interpreter::Finished || interpreter->state() == Interpreter::Aborted) { interpreter->initialize(operation); } runAct->setEnabled(false); pauseAct->setEnabled(false); abortAct->setEnabled(false); currentlyRunningConsole = true; while (!(interpreter->state() == Interpreter::Finished || interpreter->state() == Interpreter::Aborted)) { interpreter->interpret(); } currentlyRunningConsole = false; runAct->setEnabled(true); pauseAct->setEnabled(false); abortAct->setEnabled(false); QString errorMessage; if (interpreter->encounteredErrors()) { ErrorList* errorList = interpreter->getErrorList(); //qDebug() << errorList->first().text(); errorMessage = errorList->first().text(); } connect(interpreter, &Interpreter::finished, this, &MainWindow::abort); connect(interpreter, &Interpreter::treeUpdated, inspector, &Inspector::updateTree); connect(executer, &Executer::currentlyExecuting, editor, &Editor::markCurrentWord); connect(executer, &Executer::currentlyExecuting, inspector, &Inspector::markTreeNode); connect(executer, &Executer::variableTableUpdated, inspector, &Inspector::updateVariable); connect(executer, &Executer::functionTableUpdated, inspector, &Inspector::updateFunction); return errorMessage; } void MainWindow::iterate() { if (interpreter->state() == Interpreter::Finished || interpreter->state() == Interpreter::Aborted) { abort(); return; } runAct->setEnabled(false); pauseAct->setChecked(false); pauseAct->setEnabled(true); abortAct->setEnabled(true); if (interpreter->state() == Interpreter::Executing) { iterationTimer->stop(); iterationTimer->setSingleShot(true); switch (runSpeed) { case 0: iterationTimer->start(0); break; case 1: iterationTimer->start(0); break; case 2: iterationTimer->start(500); break; case 3: iterationTimer->start(1000); break; case 4: iterationTimer->start(3000); break; case 5: iterationTimer->stop(); interpreter->interpret(); pauseAct->setChecked(true); pause(); return; } } interpreter->interpret(); } void MainWindow::pause() { if (pauseAct->isChecked()) { runAct->setEnabled(true); iterationTimer->stop(); return; } iterate(); } void MainWindow::abort() { iterationTimer->stop(); interpreter->abort(); editor->removeMarkings(); inspector->clearAllMarks(); runAct->setEnabled(true); pauseAct->setChecked(false); pauseAct->setEnabled(false); abortAct->setEnabled(false); editor->enable(); console->enable(); executeConsoleAct->setEnabled(true); if (interpreter->encounteredErrors()) { errorDialog->setErrorList(interpreter->getErrorList()); showErrorDialog(true); } } void MainWindow::updateContentName(const QString& str) { QString caption = str.isEmpty() ? i18n("untitled") : str; bool modified = editor->isModified(); setWindowTitle(caption + QLatin1String("[*]")); setWindowModified(modified); statusBarFileNameLabel->setText(QString(" %1%2 ").arg(caption).arg(modified ? "*" : "")); } void MainWindow::addToRecentFiles(const QUrl &url) { recentFilesAction->addUrl(url); } void MainWindow::readConfig() { KConfigGroup config(KSharedConfig::openConfig(), "General Options"); // m_paShowStatusBar->setChecked(config->readEntry("ShowStatusBar", QVariant(false)).toBool()); // m_paShowPath->setChecked(config->readEntry("ShowPath", QVariant(false)).toBool()); recentFilesAction->loadEntries(KSharedConfig::openConfig()->group("Recent Files")); QString lang_code(config.readEntry("currentLanguageCode", QVariant(QString())).toString()); if (lang_code.isEmpty()) lang_code = "en_US"; // null-string are saved as empty-strings setCurrentLanguage(lang_code); // if(m_paShowStatusBar->isChecked()) // statusBar()->show(); // else // statusBar()->hide(); } void MainWindow::writeConfig() { KConfigGroup config(KSharedConfig::openConfig(), "General Options"); // config.writeEntry("ShowStatusBar",m_paShowStatusBar->isChecked()); // config.writeEntry("ShowPath",m_paShowPath->isChecked()); recentFilesAction->saveEntries(KSharedConfig::openConfig()->group( "Recent Files")); config.writeEntry("currentLanguageCode", currentLanguageCode); config.sync(); } void MainWindow::exportToPng() { // copied from edit code for file selection QUrl url = QFileDialog::getSaveFileUrl(this, i18n("Save as Picture"), QUrl(), QString("%1 (*.png);;%2 (*)").arg(i18n("PNG Images")).arg(i18n("All files"))); if (url.isEmpty()) return; // get our image from the canvas and save to png QImage pict = canvas->getPicture(); if (url.isLocalFile()) { pict.save(url.toLocalFile(), "PNG"); } else { pict.save(url.path(), "PNG"); } } void MainWindow::exportToSvg() { // copied from edit code for file selection // canvas->saveAsSvg() does not handle QUrl, so only local files are accepted QString path = QFileDialog::getSaveFileName(this, i18n("Save as SVG"), QString(), QString("%1 (.*svg);;%2 (*)").arg(i18n("Scalable Vector Graphics")).arg(i18n("All files"))); if (path.isEmpty()) return; canvas->saveAsSvg(windowTitle(), path); } void MainWindow::exportToHtml() { // copied from edit code for file selection // we do not handle QUrl, so only local files are accepted QString path = QFileDialog::getSaveFileName(this, i18n("Save code as HTML"), QString(), QString("*.html|%1\n*|%2").arg(i18n("HTML documents")).arg(i18n("All files"))); if (path.isEmpty()) return; QSaveFile file(path); if (!file.open(QIODevice::WriteOnly)) return; QTextStream out(&file); out << editor->toHtml(windowTitle(), currentLanguageCode); out.flush(); file.commit(); } // slots for logo functions that need to use the MainWindow class: void MainWindow::slotInputDialog(QString& value) { iterationTimer->stop(); value = QInputDialog::getText(this, i18n("Input"), i18n("Input"), QLineEdit::Normal, value); if(!currentlyRunningConsole) run(); } void MainWindow::slotMessageDialog(const QString& text) { iterationTimer->stop(); KMessageBox::information(this, text, i18n("Message")); if(!currentlyRunningConsole) run(); } void MainWindow::getNewExampleDialog() { KNS3::DownloadDialog dialog(this); dialog.exec(); updateExamplesMenu(); }