File indexing completed on 2024-04-21 05:44:06

0001 /***************************************************************************
0002  *   Copyright (C) 2005 by David Saxton                                    *
0003  *   david@bluehaze.org                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  ***************************************************************************/
0010 
0011 #include "textdocument.h"
0012 #include "asmformatter.h"
0013 #include "asminfo.h"
0014 #include "asmparser.h"
0015 #include "debugmanager.h"
0016 #include "docmanager.h"
0017 #include "documentiface.h"
0018 #include "filemetainfo.h"
0019 #include "gpsimprocessor.h"
0020 #include "ktechlab.h"
0021 #include "language.h"
0022 #include "languagemanager.h"
0023 #include "microselectwidget.h"
0024 #include "programmerdlg.h"
0025 #include "symbolviewer.h"
0026 #include "textview.h"
0027 #include "filefilters.h"
0028 
0029 // #include <kate/katedocument.h>
0030 #include <QAction>
0031 #include <QDir>
0032 #include <QTemporaryFile>
0033 
0034 #include <KLocalizedString>
0035 #include <KMessageBox>
0036 #include <KTextEditor/Document>
0037 #include <KTextEditor/Editor>
0038 #include <KXMLGUIFactory>
0039 
0040 #include <ktechlab_debug.h>
0041 
0042 bool TextDocument::isUndoAvailable() const
0043 {
0044     // return (m_doc->undoCount() != 0);
0045     return true; // TODO FIXME fix undo/redo
0046 }
0047 bool TextDocument::isRedoAvailable() const
0048 {
0049     // return (m_doc->redoCount() != 0);
0050     return true; // TODO FIXME fix undo/redo
0051 }
0052 
0053 TextDocument *TextDocument::constructTextDocument(const QString &caption)
0054 {
0055     TextDocument *textDocument = new TextDocument(caption);
0056     if (textDocument->m_constructorSuccessful)
0057         return textDocument;
0058     delete textDocument;
0059     return nullptr;
0060 }
0061 
0062 TextDocument::TextDocument(const QString &caption)
0063     : Document(caption)
0064     , m_doc(nullptr)
0065 {
0066     m_constructorSuccessful = false;
0067 
0068 #ifndef NO_GPSIM
0069     m_bOwnDebugger = false;
0070     b_lockSyncBreakpoints = false;
0071     m_lastDebugLineAt = -1;
0072     m_pDebugger = nullptr;
0073 #endif
0074 
0075     m_pLastTextOutputTarget = nullptr;
0076     m_guessedCodeType = TextDocument::ct_unknown;
0077     m_type = Document::dt_text;
0078     // m_bookmarkActions.setAutoDelete(true); // TODO see if this genereates memory leaks
0079     m_pDocumentIface = new TextDocumentIface(this);
0080     KTextEditor::Editor *editor = KTextEditor::Editor::instance();
0081     m_doc = editor->createDocument(this);
0082 
0083     if (!m_doc) {
0084         KMessageBox::error(KTechlab::self(), i18n("Failed to create editor"));
0085         return;
0086     }
0087     guessScheme();
0088 
0089     connect(m_doc, SIGNAL(undoChanged()), this, SIGNAL(undoRedoStateChanged()));
0090     connect(m_doc, SIGNAL(undoChanged()), this, SLOT(slotSyncModifiedStates()));
0091     connect(m_doc, SIGNAL(textChanged(KTextEditor::Document *)), this, SLOT(slotSyncModifiedStates()));
0092     //  connect( m_doc, SIGNAL(selectionChanged()), this, SLOT(slotSelectionmChanged()) ); // 2016.09.08 - moved to TextView
0093     connect(m_doc, SIGNAL(marksChanged(KTextEditor::Document *)), this, SLOT(slotUpdateMarksInfo()));
0094 
0095     KTextEditor::MarkInterface *markIface = qobject_cast<KTextEditor::MarkInterface *>(m_doc);
0096     if (!markIface) {
0097         KMessageBox::error(KTechlab::self(), i18n("Failed to create MarkInterface"));
0098         return;
0099     }
0100 
0101     markIface->setMarkDescription(static_cast<KTextEditor::MarkInterface::MarkTypes>(Breakpoint), i18n("Breakpoint"));
0102     markIface->setMarkPixmap(static_cast<KTextEditor::MarkInterface::MarkTypes>(Breakpoint), *inactiveBreakpointPixmap());
0103     markIface->setMarkPixmap(KTextEditor::MarkInterface::BreakpointActive, *activeBreakpointPixmap());
0104     markIface->setMarkPixmap(KTextEditor::MarkInterface::BreakpointReached, *reachedBreakpointPixmap());
0105     markIface->setMarkPixmap(KTextEditor::MarkInterface::BreakpointDisabled, *disabledBreakpointPixmap());
0106     markIface->setMarkPixmap(KTextEditor::MarkInterface::Execution, *executionPointPixmap());
0107     markIface->setEditableMarks(KTextEditor::MarkInterface::Bookmark | Breakpoint);
0108 
0109     m_constructorSuccessful = true;
0110 }
0111 
0112 TextDocument::~TextDocument()
0113 {
0114     if (!m_constructorSuccessful)
0115         return;
0116 
0117     debugStop();
0118 
0119     if (KTechlab::self()) {
0120         ViewList::iterator end = m_viewList.end();
0121         for (ViewList::iterator it = m_viewList.begin(); it != end; ++it) {
0122             if (TextView *tv = dynamic_cast<TextView *>(static_cast<View *>(*it))) {
0123                 KTextEditor::View *kv = tv->kateView();
0124                 KTechlab::self()->factory()->removeClient(kv);
0125             }
0126         }
0127     }
0128 
0129     delete m_doc;
0130     delete m_pDocumentIface;
0131 }
0132 
0133 bool TextDocument::fileClose()
0134 {
0135     const QUrl u = url();
0136     if (!u.isEmpty())
0137         fileMetaInfo()->grabMetaInfo(u, this);
0138 
0139     return Document::fileClose();
0140 }
0141 
0142 TextView *TextDocument::textView() const
0143 {
0144     return static_cast<TextView *>(activeView());
0145 }
0146 
0147 View *TextDocument::createView(ViewContainer *viewContainer, uint viewAreaId)
0148 {
0149     TextView *textView = new TextView(this, viewContainer, viewAreaId);
0150 
0151     fileMetaInfo()->initializeFromMetaInfo(url(), textView);
0152 
0153     handleNewView(textView);
0154     return textView;
0155 }
0156 
0157 KTextEditor::View *TextDocument::createKateView(QWidget *parent)
0158 {
0159     // return static_cast<KTextEditor::View*>((m_doc->createView( parent, name ))->qt_cast("Kate::View"));
0160     return m_doc->createView(parent);
0161 }
0162 
0163 void TextDocument::cut()
0164 {
0165     if (textView())
0166         textView()->cut();
0167 }
0168 void TextDocument::copy()
0169 {
0170     if (textView())
0171         textView()->copy();
0172 }
0173 void TextDocument::paste()
0174 {
0175     if (textView())
0176         textView()->paste();
0177 }
0178 
0179 void TextDocument::setText(const QString &text, bool asInitial)
0180 {
0181     if (asInitial) {
0182         disconnect(m_doc, SIGNAL(undoChanged()), this, SIGNAL(undoRedoStateChanged()));
0183         disconnect(m_doc, SIGNAL(undoChanged()), this, SLOT(slotSyncModifiedStates()));
0184         disconnect(m_doc, SIGNAL(textChanged(KTextEditor::Document *)), this, SLOT(slotSyncModifiedStates()));
0185     }
0186 
0187     const ViewList::iterator end = m_viewList.end();
0188     for (ViewList::iterator it = m_viewList.begin(); it != end; ++it)
0189         (static_cast<TextView *>(static_cast<View *>(*it)))->saveCursorPosition();
0190 
0191     m_doc->setText(text);
0192 
0193     for (ViewList::iterator it = m_viewList.begin(); it != end; ++it)
0194         (static_cast<TextView *>(static_cast<View *>(*it)))->restoreCursorPosition();
0195 
0196     if (asInitial) {
0197         // m_doc->clearUndo(); // TODO FIXME
0198         // m_doc->clearRedo(); // TODO FIXME
0199         qCWarning(KTL_LOG) << "TextDocument::clearUndo TODO";
0200         setModified(false);
0201 
0202         connect(m_doc, SIGNAL(undoChanged()), this, SIGNAL(undoRedoStateChanged()));
0203         connect(m_doc, SIGNAL(undoChanged()), this, SLOT(slotSyncModifiedStates()));
0204         connect(m_doc, SIGNAL(textChanged(KTextEditor::Document *)), this, SLOT(slotSyncModifiedStates()));
0205     }
0206 }
0207 
0208 void TextDocument::undo()
0209 {
0210     textView()->undo();
0211     slotSyncModifiedStates();
0212 }
0213 
0214 void TextDocument::redo()
0215 {
0216     textView()->redo();
0217     slotSyncModifiedStates();
0218 }
0219 
0220 void TextDocument::slotSyncModifiedStates()
0221 {
0222     setModified(m_doc->isModified());
0223 }
0224 void TextDocument::setModified(bool modified)
0225 {
0226     if ((modified == b_modified) && (modified == isModified())) {
0227         return;
0228     }
0229     m_doc->setModified(modified);
0230     b_modified = modified;
0231 
0232     emit modifiedStateChanged();
0233 }
0234 
0235 void TextDocument::guessScheme(bool allowDisable)
0236 {
0237     // And specific file actions depending on the current type of file
0238     QString fileName = url().fileName();
0239     QString extension = fileName.right(fileName.length() - fileName.lastIndexOf('.') - 1);
0240 
0241     if (extension == "asm" || extension == "src" || extension == "inc")
0242         slotInitLanguage(ct_asm);
0243 
0244     else if (extension == "hex")
0245         slotInitLanguage(ct_hex);
0246 
0247     else if (extension == "basic" || extension == "microbe")
0248         slotInitLanguage(ct_microbe);
0249 
0250     else if (extension == "c")
0251         slotInitLanguage(ct_c);
0252 
0253     else if (m_guessedCodeType != TextDocument::ct_unknown)
0254         slotInitLanguage(m_guessedCodeType);
0255 
0256     else if (allowDisable && activeView())
0257         textView()->disableActions();
0258 }
0259 
0260 void TextDocument::slotInitLanguage(CodeType type)
0261 {
0262     QString modeName;
0263 
0264     switch (type) {
0265     case ct_asm:
0266         modeName = "PicAsm";
0267         break;
0268 
0269     case ct_c:
0270         modeName = "C";
0271         break;
0272 
0273     case ct_hex:
0274         break;
0275 
0276     case ct_microbe:
0277         modeName = "Microbe";
0278         break;
0279 
0280     case ct_unknown:
0281         break;
0282     }
0283 
0284     if (!modeName.isEmpty()) {
0285         const QStringList modes = m_doc->modes();
0286         if (modes.contains(modeName)) {
0287             m_doc->setMode(modeName);
0288         }
0289     }
0290 
0291     m_guessedCodeType = type;
0292 
0293     ViewList::iterator end = m_viewList.end();
0294     for (ViewList::iterator it = m_viewList.begin(); it != end; ++it) {
0295         if (TextView *tv = dynamic_cast<TextView *>(static_cast<View *>(*it)))
0296             tv->initCodeActions();
0297     }
0298 }
0299 
0300 void TextDocument::formatAssembly()
0301 {
0302     AsmFormatter formatter;
0303     // QStringList lines = QStringList::split( "\n", m_doc->text(), true ); // 2018.12.01
0304 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
0305     QStringList lines = m_doc->text().split("\n", Qt::KeepEmptyParts);
0306 #else
0307     QStringList lines = m_doc->text().split("\n", QString::KeepEmptyParts);
0308 #endif
0309     setText(formatter.tidyAsm(lines), false);
0310     setModified(true);
0311 }
0312 
0313 void TextDocument::fileSave(const QUrl &url)
0314 {
0315     if (m_doc->url() != url) {
0316         qCCritical(KTL_LOG) << "Error: Kate::View url and passed url do not match; cannot save.";
0317         return;
0318     }
0319 
0320     if (activeView() && (textView()->save()))
0321         saveDone();
0322 }
0323 
0324 void TextDocument::fileSaveAs()
0325 {
0326     if (activeView() && (textView()->saveAs()))
0327         saveDone();
0328 
0329     // Our modified state may not have changed, but we emit this to force the
0330     // main window to update our caption.
0331     emit modifiedStateChanged();
0332 }
0333 
0334 void TextDocument::saveDone()
0335 {
0336     setURL(m_doc->url());
0337     guessScheme(false);
0338     setModified(false);
0339     emit modifiedStateChanged();
0340 }
0341 
0342 bool TextDocument::openURL(const QUrl &url)
0343 {
0344     m_doc->openUrl(url);
0345     setURL(url);
0346 
0347     fileMetaInfo()->initializeFromMetaInfo(url, this);
0348     guessScheme();
0349 
0350 #ifndef NO_GPSIM
0351     DebugManager::self()->urlOpened(this);
0352 #endif
0353 
0354     return true;
0355 }
0356 
0357 void TextDocument::setLastTextOutputTarget(TextDocument *target)
0358 {
0359     m_pLastTextOutputTarget = target;
0360 }
0361 
0362 QString TextDocument::outputFilePath(const QString &ext)
0363 {
0364     QString filePath = url().path();
0365     if (filePath.isEmpty()) {
0366         QTemporaryFile f(QDir::tempPath() + QLatin1String("/ktechlab_XXXXXX") + ext);
0367         f.setAutoRemove(false);
0368         if (!f.open()) {
0369             qCWarning(KTL_LOG) << " Failed to open temporary file";
0370             return QString();
0371         }
0372         QTextStream out(&f);
0373         out << m_doc->text();
0374         f.close();
0375         DocManager::self()->associateDocument(QUrl::fromLocalFile(f.fileName()), this);
0376         return f.fileName();
0377     }
0378     if (isModified()) {
0379         fileSave();
0380     }
0381     return filePath;
0382 }
0383 
0384 void TextDocument::slotConvertTo(QAction *action)
0385 {
0386     int target = action->data().toInt();
0387     switch (static_cast<ConvertToTarget>(target)) {
0388     case TextDocument::MicrobeOutput:
0389         break;
0390 
0391     case TextDocument::AssemblyOutput:
0392         convertToAssembly();
0393         break;
0394 
0395     case TextDocument::HexOutput:
0396         convertToHex();
0397         break;
0398 
0399     case TextDocument::PICOutput:
0400         convertToPIC();
0401         break;
0402     }
0403 }
0404 
0405 void TextDocument::convertToAssembly()
0406 {
0407     QString filePath;
0408     bool showPICSelect = false;
0409     ProcessOptions::ProcessPath::MediaType toType;
0410 
0411     if (m_guessedCodeType == TextDocument::ct_microbe) {
0412         toType = ProcessOptions::ProcessPath::AssemblyAbsolute;
0413         filePath = outputFilePath(".microbe");
0414     }
0415 
0416     else if (m_guessedCodeType == TextDocument::ct_hex) {
0417         toType = ProcessOptions::ProcessPath::Disassembly;
0418         filePath = outputFilePath(".hex");
0419     }
0420 
0421     else if (m_guessedCodeType == TextDocument::ct_c) {
0422         toType = ProcessOptions::ProcessPath::AssemblyRelocatable;
0423         filePath = outputFilePath(".c");
0424         showPICSelect = true;
0425     }
0426 
0427     else {
0428         qCCritical(KTL_LOG) << "Could not get file type for converting to assembly!";
0429         return;
0430     }
0431 
0432     OutputMethodDlg dlg(i18n("Assembly Code Output"), url(), showPICSelect, KTechlab::self());
0433 
0434     if (m_guessedCodeType == TextDocument::ct_c)
0435         dlg.microSelect()->setAllowedAsmSet(AsmInfo::PIC14 | AsmInfo::PIC16);
0436 
0437     dlg.setOutputExtension(".asm");
0438     dlg.setFileFilters({
0439         {i18n("Assembly Code") + QLatin1String(" (*.asm, *.src, *.inc)"), QStringLiteral("*.asm *.src *.inc")},
0440         {i18n("All Files"), QStringLiteral("*")},
0441     });
0442     const int accepted = dlg.exec();
0443     if (accepted != QDialog::Accepted)
0444         return;
0445 
0446     ProcessOptions o(dlg.info());
0447     o.setTextOutputTarget(m_pLastTextOutputTarget, this, SLOT(setLastTextOutputTarget(TextDocument *)));
0448     o.setInputFiles(QStringList(filePath));
0449     o.setProcessPath(ProcessOptions::ProcessPath::path(ProcessOptions::guessMediaType(filePath), toType));
0450     LanguageManager::self()->compile(o);
0451 }
0452 
0453 void TextDocument::convertToHex()
0454 {
0455     QString filePath;
0456     bool showPICSelect = false;
0457 
0458     if (m_guessedCodeType == TextDocument::ct_microbe)
0459         filePath = outputFilePath(".microbe");
0460 
0461     else if (m_guessedCodeType == TextDocument::ct_asm) {
0462         filePath = outputFilePath(".asm");
0463         showPICSelect = true; // FIXME if we use shared libs, then we need the pic type
0464     } else if (m_guessedCodeType == TextDocument::ct_c) {
0465         filePath = outputFilePath(".c");
0466         showPICSelect = true;
0467     }
0468 
0469     else {
0470         qCCritical(KTL_LOG) << "Could not get file type for converting to hex!";
0471         return;
0472     }
0473 
0474     OutputMethodDlg dlg(i18n("Hex Code Output"), url(), showPICSelect, KTechlab::self());
0475     dlg.setOutputExtension(".hex");
0476     dlg.setFileFilters({
0477         {i18n("Hex Code") + QLatin1String(" (*.hex)"), QStringLiteral("*.hex")},
0478         {i18n("All Files"), QStringLiteral("*")},
0479     });
0480 
0481     if (m_guessedCodeType == TextDocument::ct_c)
0482         dlg.microSelect()->setAllowedAsmSet(AsmInfo::PIC14 | AsmInfo::PIC16);
0483 
0484     const int accepted = dlg.exec();
0485     if (accepted != QDialog::Accepted)
0486         return;
0487 
0488     ProcessOptions o(dlg.info());
0489     o.setTextOutputTarget(m_pLastTextOutputTarget, this, SLOT(setLastTextOutputTarget(TextDocument *)));
0490     o.setInputFiles(QStringList(filePath));
0491     o.setProcessPath(ProcessOptions::ProcessPath::path(ProcessOptions::guessMediaType(filePath), ProcessOptions::ProcessPath::Program));
0492     LanguageManager::self()->compile(o);
0493 }
0494 
0495 void TextDocument::convertToPIC()
0496 {
0497     QString filePath;
0498 
0499     QString picID;
0500 
0501     switch (m_guessedCodeType) {
0502     case ct_microbe:
0503         filePath = outputFilePath(".microbe");
0504         break;
0505 
0506     case ct_asm: {
0507         filePath = outputFilePath(".asm");
0508         AsmParser p(filePath);
0509         p.parse();
0510         picID = p.picID();
0511         break;
0512     }
0513 
0514     case ct_c:
0515         filePath = outputFilePath(".c");
0516         break;
0517 
0518     case ct_hex:
0519         filePath = outputFilePath(".hex");
0520         break;
0521 
0522     case ct_unknown:
0523         qCCritical(KTL_LOG) << "Could not get file type for converting to hex!";
0524         return;
0525     }
0526 
0527     ProgrammerDlg *dlg = new ProgrammerDlg(picID, static_cast<QWidget *>(KTechlab::self()));
0528     dlg->setObjectName("Programmer Dlg");
0529 
0530     if (m_guessedCodeType == TextDocument::ct_c)
0531         dlg->microSelect()->setAllowedAsmSet(AsmInfo::PIC14 | AsmInfo::PIC16);
0532 
0533     const int accepted = dlg->exec();
0534     if (accepted != QDialog::Accepted) {
0535         dlg->deleteLater();
0536         return;
0537     }
0538 
0539     ProcessOptions o;
0540     dlg->initOptions(&o);
0541     o.setInputFiles(QStringList(filePath));
0542     o.setProcessPath(ProcessOptions::ProcessPath::path(ProcessOptions::guessMediaType(filePath), ProcessOptions::ProcessPath::Pic));
0543     LanguageManager::self()->compile(o);
0544 
0545     dlg->deleteLater();
0546 }
0547 
0548 void TextDocument::print()
0549 {
0550     // KTextEditor::printInterface(m_doc)->print (); // TODO FIXME
0551     textView()->print();
0552 }
0553 
0554 // 2016.09.08 - moved to TextView
0555 // void TextDocument::slotSelectionmChanged()
0556 // {
0557 //  KTechlab::self()->actionByName( "edit_cut" )->setEnabled( /* m_doc->hasSelection () */ m_doc->activeView()->selection() );
0558 //  KTechlab::self()->actionByName( "edit_copy" )->setEnabled( /*m_doc->hasSelection () */ m_doc->activeView()->selection() );
0559 // }
0560 
0561 IntList TextDocument::bookmarkList() const
0562 {
0563     IntList bookmarkList;
0564 
0565     typedef QHash<int, KTextEditor::Mark *> MarkList;
0566     KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface *>(m_doc);
0567     if (!iface)
0568         return IntList();
0569     // MarkList markList = m_doc->marks();
0570     const MarkList &markList = iface->marks();
0571 
0572     // Find out what marks need adding to our internal lists
0573     // for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() )
0574     for (MarkList::const_iterator itMark = markList.begin(); itMark != markList.end(); ++itMark) {
0575         const KTextEditor::Mark *mark = itMark.value();
0576         if (mark->type & KTextEditor::MarkInterface::Bookmark)
0577             bookmarkList += mark->line;
0578     }
0579 
0580     return bookmarkList;
0581 }
0582 
0583 void TextDocument::slotUpdateMarksInfo()
0584 {
0585     if (!KTechlab::self())
0586         return;
0587 
0588     if (activeView())
0589         textView()->slotUpdateMarksInfo();
0590 
0591 #ifndef NO_GPSIM
0592     syncBreakpoints();
0593 #endif
0594 
0595     // Update our list of bookmarks in the menu
0596     KTechlab::self()->unplugActionList("bookmark_actionlist");
0597     m_bookmarkActions.clear();
0598 
0599     // QPtrList<KTextEditor::Mark> markList = m_doc->marks();
0600     typedef QHash<int, KTextEditor::Mark *> MarkList;
0601     KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface *>(m_doc);
0602     if (!iface)
0603         return;
0604     // MarkList markList = m_doc->marks();
0605     MarkList markList = iface->marks();
0606 
0607     // Find out what marks need adding to our internal lists
0608     // for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() )
0609     //{
0610     for (MarkList::iterator itMark = markList.begin(); itMark != markList.end(); ++itMark) {
0611         KTextEditor::Mark *mark = itMark.value();
0612         if (mark->type & KTextEditor::MarkInterface::Bookmark) {
0613             QString actionCaption = i18n("%1 - %2", QString::number(mark->line + 1), m_doc->text(KTextEditor::Range(mark->line, 0, mark->line, 100 /* FIXME arbitrary */)));
0614             QString actionName = QString("bookmark_%1").arg(QString::number(mark->line));
0615             /*
0616             QAction * a = new QAction( actionCaption,
0617                                        0, this, SLOT(slotBookmarkRequested()), this,
0618                                        actionName );
0619                                        */
0620             QAction *a = new QAction(actionCaption, this);
0621             a->setObjectName(actionName.toLatin1().data());
0622             connect(a, SIGNAL(triggered(bool)), this, SLOT(slotBookmarkRequested()));
0623             m_bookmarkActions.append(a);
0624         }
0625     }
0626 
0627     KTechlab::self()->plugActionList("bookmark_actionlist", m_bookmarkActions);
0628 }
0629 
0630 void TextDocument::slotBookmarkRequested()
0631 {
0632     const QObject *s = sender();
0633     if (!s)
0634         return;
0635 
0636     QString name = s->objectName();
0637     if (!name.startsWith("bookmark_"))
0638         return;
0639 
0640     name.remove("bookmark_");
0641     int line = -1;
0642     bool ok;
0643     line = name.toInt(&ok);
0644     if (ok && line >= 0 && activeView())
0645         (static_cast<TextView *>(activeView()))->gotoLine(line);
0646 }
0647 
0648 void TextDocument::setBookmarks(const IntList &lines)
0649 {
0650     clearBookmarks();
0651     const IntList::const_iterator end = lines.end();
0652     for (IntList::const_iterator it = lines.begin(); it != end; ++it)
0653         setBookmark(*it, true);
0654 }
0655 
0656 void TextDocument::clearBookmarks()
0657 {
0658     // QPtrList<KTextEditor::Mark> markList = m_doc->marks();
0659     typedef QHash<int, KTextEditor::Mark *> MarkList;
0660     KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface *>(m_doc);
0661     if (!iface)
0662         return;
0663     // MarkList markList = m_doc->marks();
0664     MarkList markList = iface->marks();
0665 
0666     // Find out what marks need adding to our internal lists
0667     // for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() )
0668     //{
0669     for (MarkList::iterator itMark = markList.begin(); itMark != markList.end(); ++itMark) {
0670         KTextEditor::Mark *mark = itMark.value();
0671         if (mark->type & KTextEditor::MarkInterface::Bookmark)
0672             iface->removeMark(mark->line, KTextEditor::MarkInterface::Bookmark);
0673     }
0674 
0675     slotUpdateMarksInfo();
0676 }
0677 
0678 void TextDocument::setBookmark(uint line, bool isBookmark)
0679 {
0680     KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface *>(m_doc);
0681     if (!iface)
0682         return;
0683 
0684     if (isBookmark)
0685         iface->addMark(line, KTextEditor::MarkInterface::Bookmark);
0686 
0687     else
0688         iface->removeMark(line, KTextEditor::MarkInterface::Bookmark);
0689 }
0690 
0691 void TextDocument::setBreakpoints(const IntList &lines)
0692 {
0693 #ifndef NO_GPSIM
0694     clearBreakpoints();
0695     const IntList::const_iterator end = lines.end();
0696     for (IntList::const_iterator it = lines.begin(); it != end; ++it)
0697         setBreakpoint(*it, true);
0698 #else
0699     Q_UNUSED(lines);
0700 #endif // !NO_GPSIM
0701 }
0702 
0703 IntList TextDocument::breakpointList() const
0704 {
0705     IntList breakpointList;
0706 
0707 #ifndef NO_GPSIM
0708     // typedef QPtrList<KTextEditor::Mark> MarkList;
0709     typedef QHash<int, KTextEditor::Mark *> MarkList;
0710     KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface *>(m_doc);
0711     if (!iface)
0712         return IntList();
0713     // MarkList markList = m_doc->marks();
0714     MarkList markList = iface->marks(); // note: this will copy
0715 
0716     // Find out what marks need adding to our internal lists
0717     // for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() )
0718     for (MarkList::iterator itMark = markList.begin(); itMark != markList.end(); ++itMark) {
0719         KTextEditor::Mark *mark = itMark.value();
0720         if (mark->type & Breakpoint)
0721             breakpointList += mark->line;
0722     }
0723 #endif // !NO_GPSIM
0724 
0725     return breakpointList;
0726 }
0727 
0728 void TextDocument::setBreakpoint(uint line, bool isBreakpoint)
0729 {
0730 #ifndef NO_GPSIM
0731     KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface *>(m_doc);
0732     if (!iface)
0733         return;
0734 
0735     if (isBreakpoint) {
0736         iface->addMark(line, Breakpoint);
0737         if (m_pDebugger)
0738             m_pDebugger->setBreakpoint(m_debugFile, line, true);
0739     } else {
0740         iface->removeMark(line, Breakpoint);
0741         if (m_pDebugger)
0742             m_pDebugger->setBreakpoint(m_debugFile, line, false);
0743     }
0744 #else
0745     Q_UNUSED(line);
0746     Q_UNUSED(isBreakpoint);
0747 #endif // !NO_GPSIM
0748 }
0749 
0750 void TextDocument::debugRun()
0751 {
0752 #ifndef NO_GPSIM
0753     if (m_pDebugger) {
0754         m_pDebugger->gpsim()->setRunning(true);
0755         slotInitDebugActions();
0756         return;
0757     }
0758 
0759     switch (guessedCodeType()) {
0760     case ct_unknown:
0761         KMessageBox::error(nullptr, i18n("Unknown code type."), i18n("Cannot debug"));
0762         return;
0763 
0764     case ct_hex:
0765         KMessageBox::error(nullptr, i18n("Cannot debug hex."), i18n("Cannot debug"));
0766         return;
0767 
0768     case ct_microbe:
0769         m_bLoadDebuggerAsHLL = true;
0770         m_debugFile = outputFilePath(".microbe");
0771         break;
0772 
0773     case ct_asm:
0774         m_bLoadDebuggerAsHLL = false;
0775         m_debugFile = outputFilePath(".asm");
0776         break;
0777 
0778     case ct_c:
0779         m_bLoadDebuggerAsHLL = true;
0780         m_debugFile = outputFilePath(".c");
0781         break;
0782     }
0783 
0784     m_symbolFile = GpsimProcessor::generateSymbolFile(m_debugFile, this, SLOT(slotCODCreationSucceeded()), SLOT(slotCODCreationFailed()));
0785 #endif // !NO_GPSIM
0786 }
0787 
0788 void TextDocument::debugInterrupt()
0789 {
0790 #ifndef NO_GPSIM
0791     if (!m_pDebugger)
0792         return;
0793 
0794     m_pDebugger->gpsim()->setRunning(false);
0795     slotInitDebugActions();
0796 #endif // !NO_GPSIM
0797 }
0798 
0799 void TextDocument::debugStop()
0800 {
0801 #ifndef NO_GPSIM
0802     if (!m_pDebugger || !m_bOwnDebugger)
0803         return;
0804 
0805     m_pDebugger->gpsim()->deleteLater();
0806     m_pDebugger = nullptr;
0807     slotDebugSetCurrentLine(SourceLine());
0808     slotInitDebugActions();
0809 #endif // !NO_GPSIM
0810 }
0811 
0812 void TextDocument::debugStep()
0813 {
0814 #ifndef NO_GPSIM
0815     if (!m_pDebugger)
0816         return;
0817 
0818     m_pDebugger->stepInto();
0819 #endif // !NO_GPSIM
0820 }
0821 
0822 void TextDocument::debugStepOver()
0823 {
0824 #ifndef NO_GPSIM
0825     if (!m_pDebugger)
0826         return;
0827 
0828     m_pDebugger->stepOver();
0829 #endif // !NO_GPSIM
0830 }
0831 
0832 void TextDocument::debugStepOut()
0833 {
0834 #ifndef NO_GPSIM
0835     if (!m_pDebugger)
0836         return;
0837 
0838     m_pDebugger->stepOut();
0839 #endif // !NO_GPSIM
0840 }
0841 
0842 void TextDocument::slotDebugSetCurrentLine(const SourceLine &line)
0843 {
0844 #ifndef NO_GPSIM
0845 
0846     KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface *>(m_doc);
0847     if (!iface)
0848         return;
0849 
0850     int textLine = line.line();
0851 
0852     if (DocManager::self()->findDocument(QUrl::fromLocalFile(line.fileName())) != this)
0853         textLine = -1;
0854 
0855     iface->removeMark(m_lastDebugLineAt, KTextEditor::MarkInterface::Execution);
0856     iface->addMark(textLine, KTextEditor::MarkInterface::Execution);
0857 
0858     if (activeView())
0859         textView()->setCursorPosition(textLine, 0);
0860 
0861     m_lastDebugLineAt = textLine;
0862 #else
0863     Q_UNUSED(line);
0864 #endif // !NO_GPSIM
0865 }
0866 
0867 void TextDocument::slotInitDebugActions()
0868 {
0869 #ifndef NO_GPSIM
0870     if (m_pDebugger) {
0871         if (m_pDebugger->gpsim()->isRunning())
0872             slotDebugSetCurrentLine(SourceLine());
0873         else
0874             slotDebugSetCurrentLine(m_pDebugger->currentLine());
0875     }
0876 
0877     if (activeView())
0878         textView()->slotInitDebugActions();
0879 #endif // !NO_GPSIM
0880 }
0881 
0882 void TextDocument::slotCODCreationSucceeded()
0883 {
0884 #ifndef NO_GPSIM
0885     GpsimProcessor *gpsim = new GpsimProcessor(m_symbolFile, this);
0886 
0887     if (m_bLoadDebuggerAsHLL)
0888         gpsim->setDebugMode(GpsimDebugger::HLLDebugger);
0889     else
0890         gpsim->setDebugMode(GpsimDebugger::AsmDebugger);
0891 
0892     setDebugger(gpsim->currentDebugger(), true);
0893 #endif // !NO_GPSIM
0894 }
0895 void TextDocument::slotCODCreationFailed()
0896 {
0897 #ifndef NO_GPSIM
0898     m_debugFile = QString();
0899     m_symbolFile = QString();
0900 #endif // !NO_GPSIM
0901 }
0902 
0903 void TextDocument::slotDebuggerDestroyed()
0904 {
0905 #ifndef NO_GPSIM
0906     slotDebugSetCurrentLine(SourceLine());
0907     m_pDebugger = nullptr;
0908     m_debugFile = QString();
0909     slotInitDebugActions();
0910 #endif // !NO_GPSIM
0911 }
0912 
0913 #ifndef NO_GPSIM
0914 void TextDocument::clearBreakpoints()
0915 {
0916     // QPtrList<KTextEditor::Mark> markList = m_doc->marks();
0917     // typedef QPtrList<KTextEditor::Mark> MarkList;
0918     typedef QHash<int, KTextEditor::Mark *> MarkList;
0919     KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface *>(m_doc);
0920     if (!iface)
0921         return;
0922     // MarkList markList = m_doc->marks();
0923     MarkList markList = iface->marks(); // note: this will copy
0924 
0925     // Find out what marks need adding to our internal lists
0926     // for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() )
0927     for (MarkList::iterator itMark = markList.begin(); itMark != markList.end(); ++itMark) {
0928         KTextEditor::Mark *mark = itMark.value();
0929         if (mark->type & KTextEditor::MarkInterface::Bookmark)
0930             iface->removeMark(mark->line, Breakpoint);
0931     }
0932 
0933     slotUpdateMarksInfo();
0934 }
0935 
0936 void TextDocument::syncBreakpoints()
0937 {
0938     if (b_lockSyncBreakpoints)
0939         return;
0940 
0941     // We don't really care about synching marks if we aren't debugging / aren't able to take use of the marks
0942     if (!m_pDebugger)
0943         return;
0944 
0945     b_lockSyncBreakpoints = true;
0946 
0947     // QPtrList<KTextEditor::Mark> markList = m_doc->marks();
0948     // typedef QPtrList<KTextEditor::Mark> MarkList;
0949     typedef QHash<int, KTextEditor::Mark *> MarkList;
0950     KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface *>(m_doc);
0951     if (!iface)
0952         return;
0953     // MarkList markList = m_doc->marks();
0954     MarkList markList = iface->marks(); // note: this will copy
0955 
0956     IntList bpList;
0957 
0958     // Find out what marks need adding to our internal lists
0959     // for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() )
0960     for (MarkList::iterator itMark = markList.begin(); itMark != markList.end(); ++itMark) {
0961         KTextEditor::Mark *mark = itMark.value();
0962         const int line = mark->line;
0963 
0964         if (mark->type & Breakpoint)
0965             bpList.append(line);
0966 
0967         if (mark->type == KTextEditor::MarkInterface::Execution)
0968             m_lastDebugLineAt = line;
0969     }
0970 
0971     m_pDebugger->setBreakpoints(m_debugFile, bpList);
0972 
0973     b_lockSyncBreakpoints = false;
0974 }
0975 
0976 bool TextDocument::debuggerIsRunning() const
0977 {
0978     return m_pDebugger;
0979 }
0980 
0981 bool TextDocument::debuggerIsStepping() const
0982 {
0983     return m_pDebugger && !m_pDebugger->gpsim()->isRunning();
0984 }
0985 
0986 void TextDocument::setDebugger(GpsimDebugger *debugger, bool ownDebugger)
0987 {
0988     if (debugger == m_pDebugger)
0989         return;
0990 
0991     // If we create a gpsim, then we may get called by DebugManager, which will
0992     // try to claim we don't own it. So if we have a symbol file waiting, thne
0993     // wait until we are called from its successful creation
0994     if (!m_symbolFile.isEmpty() && !ownDebugger)
0995         return;
0996 
0997     // Reset it for use next time
0998     m_symbolFile = QString();
0999 
1000     if (m_bOwnDebugger)
1001         delete m_pDebugger;
1002 
1003     m_pDebugger = debugger;
1004     m_bOwnDebugger = ownDebugger;
1005 
1006     if (!m_pDebugger)
1007         return;
1008 
1009     if (m_debugFile.isEmpty())
1010         m_debugFile = url().path();
1011 
1012     connect(m_pDebugger, &GpsimDebugger::destroyed, this, &TextDocument::slotDebuggerDestroyed);
1013     connect(m_pDebugger->gpsim(), &GpsimProcessor::runningStatusChanged, this, &TextDocument::slotInitDebugActions);
1014     connect(m_pDebugger, &GpsimDebugger::lineReached, this, &TextDocument::slotDebugSetCurrentLine);
1015     m_pDebugger->setBreakpoints(m_debugFile, breakpointList());
1016 
1017     slotInitDebugActions();
1018     if (!m_pDebugger->gpsim()->isRunning())
1019         slotDebugSetCurrentLine(m_pDebugger->currentLine());
1020 
1021     if (this == dynamic_cast<TextDocument *>(DocManager::self()->getFocusedDocument()))
1022         SymbolViewer::self()->setContext(m_pDebugger->gpsim());
1023 }
1024 #endif // !NO_GPSIM
1025 
1026 const QPixmap *TextDocument::inactiveBreakpointPixmap()
1027 {
1028     const char *breakpoint_gr_xpm[] = {"11 16 6 1",   "c c #c6c6c6", "d c #2c2c2c", "# c #000000", ". c None",    "a c #ffffff", "b c #555555", "...........", "...........", "...#####...", "..#aaaaa#..", ".#abbbbbb#.",
1029                                        "#abbbbbbbb#", "#abcacacbd#", "#abbbbbbbb#", "#abcacacbd#", "#abbbbbbbb#", ".#bbbbbbb#.", "..#bdbdb#..", "...#####...", "...........", "...........", "..........."};
1030     static QPixmap pixmap(breakpoint_gr_xpm);
1031     return &pixmap;
1032 }
1033 
1034 const QPixmap *TextDocument::activeBreakpointPixmap()
1035 {
1036     const char *breakpoint_xpm[] = {"11 16 6 1",   "c c #c6c6c6", ". c None",    "# c #000000", "d c #840000", "a c #ffffff", "b c #ff0000", "...........", "...........", "...#####...", "..#aaaaa#..", ".#abbbbbb#.",
1037                                     "#abbbbbbbb#", "#abcacacbd#", "#abbbbbbbb#", "#abcacacbd#", "#abbbbbbbb#", ".#bbbbbbb#.", "..#bdbdb#..", "...#####...", "...........", "...........", "..........."};
1038     static QPixmap pixmap(breakpoint_xpm);
1039     return &pixmap;
1040 }
1041 
1042 const QPixmap *TextDocument::reachedBreakpointPixmap()
1043 {
1044     const char *breakpoint_bl_xpm[] = {"11 16 7 1",   "a c #c0c0ff", "# c #000000", "c c #0000c0", "e c #0000ff", "b c #dcdcdc", "d c #ffffff", ". c None",    "...........", "...........", "...#####...", "..#ababa#..",
1045                                        ".#bcccccc#.", "#acccccccc#", "#bcadadace#", "#acccccccc#", "#bcadadace#", "#acccccccc#", ".#ccccccc#.", "..#cecec#..", "...#####...", "...........", "...........", "..........."};
1046     static QPixmap pixmap(breakpoint_bl_xpm);
1047     return &pixmap;
1048 }
1049 
1050 const QPixmap *TextDocument::disabledBreakpointPixmap()
1051 {
1052     const char *breakpoint_wh_xpm[] = {"11 16 7 1",   "a c #c0c0ff", "# c #000000", "c c #0000c0", "e c #0000ff", "b c #dcdcdc", "d c #ffffff", ". c None",    "...........", "...........", "...#####...", "..#ddddd#..",
1053                                        ".#ddddddd#.", "#ddddddddd#", "#ddddddddd#", "#ddddddddd#", "#ddddddddd#", "#ddddddddd#", ".#ddddddd#.", "..#ddddd#..", "...#####...", "...........", "...........", "..........."};
1054     static QPixmap pixmap(breakpoint_wh_xpm);
1055     return &pixmap;
1056 }
1057 
1058 const QPixmap *TextDocument::executionPointPixmap()
1059 {
1060     const char *exec_xpm[] = {"11 16 4 1",   "a c #00ff00", "b c #000000", ". c None",    "# c #00c000", "...........", "...........", "...........", "#a.........", "#aaa.......", "#aaaaa.....",
1061                               "#aaaaaaa...", "#aaaaaaaaa.", "#aaaaaaa#b.", "#aaaaa#b...", "#aaa#b.....", "#a#b.......", "#b.........", "...........", "...........", "..........."};
1062     static QPixmap pixmap(exec_xpm);
1063     return &pixmap;
1064 }
1065 
1066 #include "moc_textdocument.cpp"