Warning, file /sdk/ktechlab/src/textdocument.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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