1 /***************************************************************************
2  *   Copyright (C) 2005 by David Saxton                                    *
3  *   david@bluehaze.org                                                    *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  ***************************************************************************/
10 
11 #include "asmformatter.h"
12 #include "asminfo.h"
13 #include "asmparser.h"
14 #include "debugmanager.h"
15 #include "docmanager.h"
16 #include "documentiface.h"
17 #include "filemetainfo.h"
18 #include "gpsimprocessor.h"
19 #include "ktechlab.h"
20 #include "language.h"
21 #include "languagemanager.h"
22 #include "microselectwidget.h"
23 #include "programmerdlg.h"
24 #include "symbolviewer.h"
25 #include "textdocument.h"
26 #include "textview.h"
27 #include "gpsimprocessor.h"
28 
29 // #include <kate/katedocument.h>
30 #include <QAction>
31 #include <QDebug>
32 #include <QTemporaryFile>
33 #include <QDir>
34 
35 #include <KLocalizedString>
36 #include <KMessageBox>
37 #include <KTextEditor/Document>
38 #include <KXMLGUIFactory>
39 #include <KTextEditor/Editor>
40 
41 
isUndoAvailable() const42 bool TextDocument::isUndoAvailable() const {
43     //return (m_doc->undoCount() != 0);
44     return true; // TODO FIXME fix undo/redo
45 }
isRedoAvailable() const46 bool TextDocument::isRedoAvailable() const {
47     //return (m_doc->redoCount() != 0);
48     return true; // TODO FIXME fix undo/redo
49 
50 }
51 
constructTextDocument(const QString & caption,const char * name)52 TextDocument *TextDocument::constructTextDocument( const QString& caption, const char *name )
53 {
54 	TextDocument *textDocument = new TextDocument( caption, name);
55 	if( textDocument->m_constructorSuccessful )
56 		return textDocument;
57 	delete textDocument;
58 	return nullptr;
59 }
60 
61 
TextDocument(const QString & caption,const char * name)62 TextDocument::TextDocument( const QString &caption, const char *name )
63 	: Document( caption, name ),
64 	  m_doc(nullptr)
65 {
66 	m_constructorSuccessful = false;
67 
68 #ifndef NO_GPSIM
69 	m_bOwnDebugger = false;
70 	b_lockSyncBreakpoints = false;
71 	m_lastDebugLineAt = -1;
72 	m_pDebugger = nullptr;
73 #endif
74 
75 	m_pLastTextOutputTarget = nullptr;
76 	m_guessedCodeType = TextDocument::ct_unknown;
77 	m_type = Document::dt_text;
78 	//m_bookmarkActions.setAutoDelete(true); // TODO see if this genereates memory leaks
79 	m_pDocumentIface = new TextDocumentIface(this);
80     KTextEditor::Editor* editor = KTextEditor::Editor::instance();
81     m_doc = editor->createDocument(this);
82 
83     if(!m_doc)
84     {
85         KMessageBox::sorry( KTechlab::self(), i18n("Failed to create editor") );
86         return;
87     }
88 	guessScheme();
89 
90 	connect( m_doc, SIGNAL(undoChanged()),		this, SIGNAL(undoRedoStateChanged()) );
91 	connect( m_doc, SIGNAL(undoChanged()),		this, SLOT(slotSyncModifiedStates()) );
92 	connect( m_doc, SIGNAL(textChanged(KTextEditor::Document *)),		this, SLOT(slotSyncModifiedStates()) );
93 // 	connect( m_doc,	SIGNAL(selectionChanged()),	this, SLOT(slotSelectionmChanged()) ); // 2016.09.08 - moved to TextView
94     connect( m_doc, SIGNAL(marksChanged(KTextEditor::Document*)),     this, SLOT(slotUpdateMarksInfo()) );
95 
96     KTextEditor::MarkInterface *markIface = qobject_cast<KTextEditor::MarkInterface*>( m_doc );
97     if (!markIface) {
98         KMessageBox::sorry( KTechlab::self(), i18n("Failed to create MarkInterface") );
99         return;
100     }
101 
102 	markIface->setMarkDescription((KTextEditor::MarkInterface::MarkTypes)Breakpoint, i18n("Breakpoint"));
103 	markIface->setMarkPixmap((KTextEditor::MarkInterface::MarkTypes)Breakpoint, *inactiveBreakpointPixmap());
104 	markIface->setMarkPixmap((KTextEditor::MarkInterface::MarkTypes)ActiveBreakpoint, *activeBreakpointPixmap());
105 	markIface->setMarkPixmap((KTextEditor::MarkInterface::MarkTypes)ReachedBreakpoint, *reachedBreakpointPixmap());
106 	markIface->setMarkPixmap((KTextEditor::MarkInterface::MarkTypes)DisabledBreakpoint, *disabledBreakpointPixmap());
107 	markIface->setMarkPixmap((KTextEditor::MarkInterface::MarkTypes)ExecutionPoint, *executionPointPixmap());
108 	markIface->setEditableMarks( Bookmark | Breakpoint );
109 
110 	m_constructorSuccessful = true;
111 }
112 
113 
~TextDocument()114 TextDocument::~TextDocument()
115 {
116 	if( !m_constructorSuccessful ) return;
117 
118 	debugStop();
119 
120 	if ( KTechlab::self() )
121 	{
122 		ViewList::iterator end = m_viewList.end();
123 		for ( ViewList::iterator it = m_viewList.begin(); it != end; ++it )
124 		{
125 			if ( TextView * tv = dynamic_cast<TextView*>( (View*)*it) )
126 			{
127 				KTextEditor::View * kv = tv->kateView();
128 				KTechlab::self()->factory()->removeClient( kv );
129 			}
130 		}
131 	}
132 
133 	delete m_doc;
134 	delete m_pDocumentIface;
135 }
136 
137 
fileClose()138 bool TextDocument::fileClose()
139 {
140 	const QUrl u = url();
141 	if ( !u.isEmpty() )
142 		fileMetaInfo()->grabMetaInfo( u, this );
143 
144 	return Document::fileClose();
145 }
146 
147 
textView() const148 TextView* TextDocument::textView() const
149 {
150 	return static_cast<TextView*>(activeView());
151 }
152 
153 
createView(ViewContainer * viewContainer,uint viewAreaId,const char * name)154 View * TextDocument::createView( ViewContainer *viewContainer, uint viewAreaId, const char *name )
155 {
156 	TextView * textView = new TextView( this, viewContainer, viewAreaId, name );
157 
158 	fileMetaInfo()->initializeFromMetaInfo( url(), textView );
159 
160 	handleNewView(textView);
161 	return textView;
162 }
163 
164 
createKateView(QWidget * parent,const char *)165 KTextEditor::View* TextDocument::createKateView( QWidget *parent, const char * /*name*/ )
166 {
167 	//return static_cast<KTextEditor::View*>((m_doc->createView( parent, name ))->qt_cast("Kate::View"));
168     return m_doc->createView( parent /*, name */);
169 }
170 
171 
cut()172 void TextDocument::cut()
173 {
174 	if (textView())
175 		textView()->cut();
176 }
copy()177 void TextDocument::copy()
178 {
179 	if (textView())
180 		textView()->copy();
181 }
paste()182 void TextDocument::paste()
183 {
184 	if (textView())
185 		textView()->paste();
186 }
187 
188 
setText(const QString & text,bool asInitial)189 void TextDocument::setText( const QString & text, bool asInitial )
190 {
191 	if ( asInitial )
192 	{
193 		disconnect( m_doc, SIGNAL(undoChanged()), this, SIGNAL(undoRedoStateChanged()) );
194 		disconnect( m_doc, SIGNAL(undoChanged()), this, SLOT(slotSyncModifiedStates()) );
195 		disconnect( m_doc, SIGNAL(textChanged(KTextEditor::Document *)), this, SLOT(slotSyncModifiedStates()) );
196 	}
197 
198 	const ViewList::iterator end = m_viewList.end();
199 	for ( ViewList::iterator it = m_viewList.begin(); it != end; ++it )
200 		(static_cast<TextView*>((View*)*it))->saveCursorPosition();
201 
202 	m_doc->setText(text);
203 
204 	for ( ViewList::iterator it = m_viewList.begin(); it != end; ++it )
205 		(static_cast<TextView*>((View*)*it))->restoreCursorPosition();
206 
207 	if ( asInitial )
208 	{
209 		//m_doc->clearUndo(); // TODO FIXME
210 		//m_doc->clearRedo(); // TODO FIXME
211         qWarning() << "TextDocument::clearUndo TODO";
212 		setModified(false);
213 
214 		connect( m_doc, SIGNAL(undoChanged()), this, SIGNAL(undoRedoStateChanged()) );
215 		connect( m_doc, SIGNAL(undoChanged()), this, SLOT(slotSyncModifiedStates()) );
216 		connect( m_doc, SIGNAL(textChanged(KTextEditor::Document *)), this, SLOT(slotSyncModifiedStates()) );
217 	}
218 }
219 
220 
undo()221 void TextDocument::undo()
222 {
223     textView()->undo();
224 	slotSyncModifiedStates();
225 }
226 
227 
redo()228 void TextDocument::redo()
229 {
230     textView()->redo();
231 	slotSyncModifiedStates();
232 }
233 
234 
slotSyncModifiedStates()235 void TextDocument::slotSyncModifiedStates()
236 {
237 	setModified( m_doc->isModified() );
238 }
setModified(bool modified)239 void TextDocument::setModified( bool modified )
240 {
241 	if ( (modified == b_modified) && (modified == isModified()) ) {
242 		return;
243 	}
244 	m_doc->setModified(modified);
245 	b_modified = modified;
246 
247 	emit modifiedStateChanged();
248 }
249 
250 
guessScheme(bool allowDisable)251 void TextDocument::guessScheme( bool allowDisable )
252 {
253 	// And specific file actions depending on the current type of file
254 	QString fileName = url().fileName();
255 	QString extension = fileName.right( fileName.length() - fileName.lastIndexOf('.') - 1 );
256 
257 	if ( extension == "asm" || extension == "src" || extension == "inc" )
258 		slotInitLanguage(ct_asm);
259 
260 	else if ( extension == "hex" )
261 		slotInitLanguage(ct_hex);
262 
263 	else if ( extension == "basic" || extension == "microbe" )
264 		slotInitLanguage(ct_microbe);
265 
266 	else if ( extension == "c" )
267 		slotInitLanguage(ct_c);
268 
269 	else if ( m_guessedCodeType != TextDocument::ct_unknown )
270 		slotInitLanguage(m_guessedCodeType);
271 
272 	else if ( allowDisable && activeView() )
273 		textView()->disableActions();
274 }
275 
276 
slotInitLanguage(CodeType type)277 void TextDocument::slotInitLanguage( CodeType type )
278 {
279 	QString hlName;
280 
281 	switch (type)
282 	{
283 		case ct_asm:
284 			hlName = "PicAsm";
285 			break;
286 
287 		case ct_c:
288 			hlName = "C";
289 			break;
290 
291 		case ct_hex:
292 			break;
293 
294 		case ct_microbe:
295 			hlName = "Microbe";
296 			break;
297 
298 		case ct_unknown:
299 			break;
300 	}
301 
302 	if ( !hlName.isEmpty() )
303 	{
304 		//int i = 0; // 2017.10.01 - comment out unused variable
305 		//int hlModeCount = m_doc->hlModeCount();
306         QStringList hlModes = m_doc->highlightingModes();
307 		//while ( i<hlModeCount && m_doc->hlModeName(i) != hlName )
308 		//	i++;
309         while (!hlModes.isEmpty()) {
310             if (hlModes.first() == hlName) {
311                 break;
312             }
313             hlModes.removeFirst();
314         }
315 
316 		//m_doc->setHlMode(i);
317 		if (!hlModes.isEmpty()) {
318             m_doc->setHighlightingMode(hlModes.first());
319         }
320 	}
321 
322 	m_guessedCodeType = type;
323 
324 	ViewList::iterator end = m_viewList.end();
325 	for ( ViewList::iterator it = m_viewList.begin(); it != end; ++it )
326 	{
327 		if ( TextView * tv = dynamic_cast<TextView*>( (View*)*it ) )
328 			tv->initCodeActions();
329 	}
330 }
331 
332 
formatAssembly()333 void TextDocument::formatAssembly()
334 {
335 	AsmFormatter formatter;
336 	//QStringList lines = QStringList::split( "\n", m_doc->text(), true ); // 2018.12.01
337     QStringList lines = m_doc->text().split("\n", QString::KeepEmptyParts);
338 	setText( formatter.tidyAsm(lines), false );
339 	setModified(true);
340 }
341 
342 
fileSave(const QUrl & url)343 void TextDocument::fileSave( const QUrl& url )
344 {
345 	if ( m_doc->url() != url )
346 	{
347 		qCritical() << Q_FUNC_INFO << "Error: Kate::View url and passed url do not match; cannot save." << endl;
348 		return;
349 	}
350 
351 	if ( activeView() && (textView()->save()) )
352 		saveDone();
353 }
354 
355 
fileSaveAs()356 void TextDocument::fileSaveAs()
357 {
358 	if (  activeView() && (textView()->saveAs()) )
359 		saveDone();
360 
361 	// Our modified state may not have changed, but we emit this to force the
362 	// main window to update our caption.
363 	emit modifiedStateChanged();
364 }
365 
366 
saveDone()367 void TextDocument::saveDone()
368 {
369 	setURL( m_doc->url() );
370 	guessScheme(false);
371 	setModified(false);
372 	emit modifiedStateChanged();
373 }
374 
375 
openURL(const QUrl & url)376 bool TextDocument::openURL( const QUrl& url )
377 {
378 	m_doc->openUrl(url);
379 	setURL(url);
380 
381 	fileMetaInfo()->initializeFromMetaInfo( url, this );
382 	guessScheme();
383 
384 #ifndef NO_GPSIM
385 	DebugManager::self()->urlOpened( this );
386 #endif
387 
388 	return true;
389 }
390 
391 
setLastTextOutputTarget(TextDocument * target)392 void TextDocument::setLastTextOutputTarget( TextDocument * target )
393 {
394 	m_pLastTextOutputTarget = target;
395 }
396 
397 
outputFilePath(const QString & ext)398 QString TextDocument::outputFilePath( const QString &ext )
399 {
400 	QString filePath = url().path();
401 	if ( filePath.isEmpty() )
402 	{
403         QTemporaryFile f(QDir::tempPath() + QLatin1String("/ktechlab_XXXXXX") + ext);
404         f.setAutoRemove(false);
405         if (!f.open()) {
406             qWarning() << Q_FUNC_INFO << " Failed to open temporary file";
407             return QString();
408         }
409         QTextStream out(&f);
410         out << m_doc->text();
411 		f.close();
412 		DocManager::self()->associateDocument(QUrl::fromLocalFile(f.fileName()), this );
413 		return f.fileName();
414 	}
415 	if ( isModified() )
416 	{
417 		fileSave();
418 	}
419 	return filePath;
420 }
421 
422 
slotConvertTo(QAction * action)423 void TextDocument::slotConvertTo( QAction *action )
424 {
425     int target = action->data().toInt();
426 	switch ( (ConvertToTarget)target )
427 	{
428 		case TextDocument::MicrobeOutput:
429 			break;
430 
431 		case TextDocument::AssemblyOutput:
432 			convertToAssembly();
433 			break;
434 
435 		case TextDocument::HexOutput:
436 			convertToHex();
437 			break;
438 
439 		case TextDocument::PICOutput:
440 			convertToPIC();
441 			break;
442 	}
443 }
444 
445 
convertToAssembly()446 void TextDocument::convertToAssembly()
447 {
448 	QString filePath;
449 	bool showPICSelect = false;
450 	ProcessOptions::ProcessPath::MediaType toType;
451 
452 	if ( m_guessedCodeType == TextDocument::ct_microbe )
453 	{
454 		toType = ProcessOptions::ProcessPath::AssemblyAbsolute;
455 		filePath = outputFilePath(".microbe");
456 	}
457 
458 	else if ( m_guessedCodeType == TextDocument::ct_hex )
459 	{
460 		toType = ProcessOptions::ProcessPath::Disassembly;
461 		filePath = outputFilePath(".hex");
462 	}
463 
464 	else if ( m_guessedCodeType == TextDocument::ct_c )
465 	{
466 		toType = ProcessOptions::ProcessPath::AssemblyRelocatable;
467 		filePath = outputFilePath(".c");
468 		showPICSelect = true;
469 	}
470 
471 	else
472 	{
473 		qCritical() << "Could not get file type for converting to assembly!"<<endl;
474 		return;
475 	}
476 
477 	OutputMethodDlg dlg( i18n("Assembly Code Output"), url(), showPICSelect, KTechlab::self() );
478 
479 	if ( m_guessedCodeType == TextDocument::ct_c )
480 		dlg.microSelect()->setAllowedAsmSet( AsmInfo::PIC14 | AsmInfo::PIC16 );
481 
482 	dlg.setOutputExtension(".asm");
483 	dlg.setFilter( QString("*.asm *.src *.inc|%1 (*.asm, *.src, *.inc)\n*|%2").arg(i18n("Assembly Code")).arg(i18n("All Files")) );
484     const int accepted = dlg.exec();
485     if (accepted != QDialog::Accepted)
486 		return;
487 
488 	ProcessOptions o( dlg.info() );
489 	o.setTextOutputTarget( m_pLastTextOutputTarget, this, SLOT(setLastTextOutputTarget( TextDocument* )) );
490 	o.setInputFiles(QStringList( filePath) );
491 	o.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::guessMediaType(filePath), toType ) );
492 	LanguageManager::self()->compile(o);
493 }
494 
495 
convertToHex()496 void TextDocument::convertToHex()
497 {
498 	QString filePath;
499 	bool showPICSelect = false;
500 
501 	if ( m_guessedCodeType == TextDocument::ct_microbe )
502 		filePath = outputFilePath(".microbe");
503 
504 	else if ( m_guessedCodeType == TextDocument::ct_asm )
505 	{
506 		filePath = outputFilePath(".asm");
507 		showPICSelect = true;	// FIXME if we use shared libs, then we need the pic type
508 	}
509 	else if ( m_guessedCodeType == TextDocument::ct_c )
510 	{
511 		filePath = outputFilePath(".c");
512 		showPICSelect = true;
513 	}
514 
515 	else
516 	{
517 		qCritical() << "Could not get file type for converting to hex!"<<endl;
518 		return;
519 	}
520 
521 	OutputMethodDlg dlg( i18n("Hex Code Output"), url(), showPICSelect, KTechlab::self() );
522 	dlg.setOutputExtension(".hex");
523 	dlg.setFilter( QString("*.hex|Hex (*.hex)\n*|%1").arg(i18n("All Files")) );
524 
525 	if ( m_guessedCodeType == TextDocument::ct_c )
526 		dlg.microSelect()->setAllowedAsmSet( AsmInfo::PIC14 | AsmInfo::PIC16 );
527 
528     const int accepted = dlg.exec();
529     if (accepted != QDialog::Accepted)
530 		return;
531 
532 	ProcessOptions o( dlg.info() );
533 	o.setTextOutputTarget( m_pLastTextOutputTarget, this, SLOT(setLastTextOutputTarget( TextDocument* )) );
534 	o.setInputFiles(QStringList(filePath));
535 	o.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::guessMediaType(filePath), ProcessOptions::ProcessPath::Program ) );
536 	LanguageManager::self()->compile(o);
537 }
538 
539 
convertToPIC()540 void TextDocument::convertToPIC()
541 {
542 	QString filePath;
543 
544 	QString picID;
545 
546 	switch ( m_guessedCodeType )
547 	{
548 		case ct_microbe:
549 			filePath = outputFilePath(".microbe");
550 			break;
551 
552 		case ct_asm:
553 		{
554 			filePath = outputFilePath(".asm");
555 			AsmParser p( filePath );
556 			p.parse();
557 			picID = p.picID();
558 			break;
559 		}
560 
561 		case ct_c:
562 			filePath = outputFilePath(".c");
563 			break;
564 
565 		case ct_hex:
566 			filePath = outputFilePath(".hex");
567 			break;
568 
569 		case ct_unknown:
570 			qCritical() << "Could not get file type for converting to hex!"<<endl;
571 			return;
572 	}
573 
574 	ProgrammerDlg * dlg = new ProgrammerDlg( picID, (QWidget*)KTechlab::self(), "Programmer Dlg" );
575 
576 	if ( m_guessedCodeType == TextDocument::ct_c )
577 		dlg->microSelect()->setAllowedAsmSet( AsmInfo::PIC14 | AsmInfo::PIC16 );
578 
579     const int accepted = dlg->exec();
580     if (accepted != QDialog::Accepted) {
581 		dlg->deleteLater();
582 		return;
583 	}
584 
585 	ProcessOptions o;
586 	dlg->initOptions( & o );
587 	o.setInputFiles( QStringList(filePath ));
588 	o.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::guessMediaType(filePath), ProcessOptions::ProcessPath::Pic ) );
589 	LanguageManager::self()->compile( o );
590 
591 	dlg->deleteLater();
592 }
593 
594 
print()595 void TextDocument::print()
596 {
597 	//KTextEditor::printInterface(m_doc)->print (); // TODO FIXME
598     textView()->print();
599 }
600 
601 // 2016.09.08 - moved to TextView
602 // void TextDocument::slotSelectionmChanged()
603 // {
604 // 	KTechlab::self()->actionByName( "edit_cut" )->setEnabled( /* m_doc->hasSelection () */ m_doc->activeView()->selection() );
605 // 	KTechlab::self()->actionByName( "edit_copy" )->setEnabled( /*m_doc->hasSelection () */ m_doc->activeView()->selection() );
606 // }
607 
608 
bookmarkList() const609 IntList TextDocument::bookmarkList() const
610 {
611 	IntList bookmarkList;
612 
613 	typedef QHash<int, KTextEditor::Mark*> MarkList;
614     KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface*>( m_doc );
615     if (!iface) return IntList();
616 	//MarkList markList = m_doc->marks();
617     const MarkList &markList = iface->marks();
618 
619 	// Find out what marks need adding to our internal lists
620 	//for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() )
621     for (MarkList::const_iterator itMark = markList.begin(); itMark != markList.end(); ++itMark)
622 	{
623         const KTextEditor::Mark * mark = itMark.value();
624 		if ( mark->type & Bookmark )
625 			bookmarkList += mark->line;
626 	}
627 
628 	return bookmarkList;
629 }
630 
631 
slotUpdateMarksInfo()632 void TextDocument::slotUpdateMarksInfo()
633 {
634 	if ( !KTechlab::self() )
635 		return;
636 
637 	if ( activeView() )
638 		textView()->slotUpdateMarksInfo();
639 
640 #ifndef NO_GPSIM
641 	syncBreakpoints();
642 #endif
643 
644 	// Update our list of bookmarks in the menu
645 	KTechlab::self()->unplugActionList("bookmark_actionlist");
646 	m_bookmarkActions.clear();
647 
648 	//QPtrList<KTextEditor::Mark> markList = m_doc->marks();
649     typedef QHash<int, KTextEditor::Mark*> MarkList;
650     KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface*>( m_doc );
651     if (!iface) return ;
652     //MarkList markList = m_doc->marks();
653     MarkList markList = iface->marks();
654 
655 
656 	// Find out what marks need adding to our internal lists
657 	//for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() )
658 	//{
659     for (MarkList::iterator itMark = markList.begin(); itMark != markList.end(); ++itMark)
660     {
661         KTextEditor::Mark * mark = itMark.value();
662 		if ( mark->type & Bookmark )
663 		{
664             QString actionCaption = i18n("%1 - %2", QString::number( mark->line+1 ),
665                                             m_doc->text(KTextEditor::Range( mark->line, 0, mark->line, 100 /* FIXME arbitrary */)) );
666             QString actionName = QString("bookmark_%1").arg(QString::number(mark->line));
667             /*
668 			QAction * a = new QAction( actionCaption,
669 									   0, this, SLOT(slotBookmarkRequested()), this,
670 									   actionName );
671 									   */
672             QAction * a = new QAction( actionCaption, this);
673             a->setObjectName(actionName.toLatin1().data());
674             connect(a, SIGNAL(triggered(bool)), this, SLOT(slotBookmarkRequested()));
675 			m_bookmarkActions.append(a);
676 		}
677 	}
678 
679 	KTechlab::self()->plugActionList( "bookmark_actionlist", m_bookmarkActions );
680 }
681 
682 
slotBookmarkRequested()683 void TextDocument::slotBookmarkRequested()
684 {
685 	const QObject * s = sender();
686 	if (!s) return;
687 
688 	QString name = s->objectName();
689 	if ( !name.startsWith("bookmark_") )
690 		return;
691 
692 	name.remove("bookmark_");
693 	int line = -1;
694 	bool ok;
695 	line = name.toInt(&ok);
696 	if ( ok && line >= 0 && activeView() )
697 		(static_cast<TextView*>(activeView()))->gotoLine(line);
698 }
699 
700 
setBookmarks(const IntList & lines)701 void TextDocument::setBookmarks( const IntList &lines )
702 {
703 	clearBookmarks();
704 	const IntList::const_iterator end = lines.end();
705 	for ( IntList::const_iterator it = lines.begin(); it != end; ++it )
706 		setBookmark( *it, true );
707 }
708 
709 
clearBookmarks()710 void TextDocument::clearBookmarks()
711 {
712 	//QPtrList<KTextEditor::Mark> markList = m_doc->marks();
713     typedef QHash<int, KTextEditor::Mark*> MarkList;
714     KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface*>( m_doc );
715     if (!iface) return;
716     //MarkList markList = m_doc->marks();
717     MarkList markList = iface->marks();
718 
719 	// Find out what marks need adding to our internal lists
720 	//for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() )
721 	//{
722     for (MarkList::iterator itMark = markList.begin(); itMark != markList.end(); ++itMark)
723     {
724         KTextEditor::Mark * mark = itMark.value();
725 		if ( mark->type & Bookmark )
726 			iface->removeMark( mark->line, Bookmark );
727 	}
728 
729 	slotUpdateMarksInfo();
730 }
731 
732 
setBookmark(uint line,bool isBookmark)733 void TextDocument::setBookmark( uint line, bool isBookmark )
734 {
735     KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface*>( m_doc );
736     if (!iface) return;
737 
738 	if (isBookmark)
739 		iface->addMark( line, Bookmark );
740 
741 	else
742 		iface->removeMark( line, Bookmark );
743 }
744 
745 
setBreakpoints(const IntList & lines)746 void TextDocument::setBreakpoints( const IntList &lines )
747 {
748 #ifndef NO_GPSIM
749 	clearBreakpoints();
750 	const IntList::const_iterator end = lines.end();
751 	for ( IntList::const_iterator it = lines.begin(); it != end; ++it )
752 		setBreakpoint( *it, true );
753 #endif // !NO_GPSIM
754 }
755 
756 
breakpointList() const757 IntList TextDocument::breakpointList() const
758 {
759 	IntList breakpointList;
760 
761 #ifndef NO_GPSIM
762 	//typedef QPtrList<KTextEditor::Mark> MarkList;
763     typedef QHash<int, KTextEditor::Mark*> MarkList;
764     KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface*>( m_doc );
765     if (!iface) return IntList();
766     //MarkList markList = m_doc->marks();
767     MarkList markList = iface->marks(); // note: this will copy
768 
769 
770 	// Find out what marks need adding to our internal lists
771 	//for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() )
772     for (MarkList::iterator itMark = markList.begin(); itMark != markList.end(); ++itMark)
773 	{
774         KTextEditor::Mark * mark = itMark.value();
775 		if ( mark->type & Breakpoint )
776 			breakpointList += mark->line;
777 	}
778 #endif // !NO_GPSIM
779 
780 	return breakpointList;
781 }
782 
783 
setBreakpoint(uint line,bool isBreakpoint)784 void TextDocument::setBreakpoint( uint line, bool isBreakpoint )
785 {
786 #ifndef NO_GPSIM
787     KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface*>( m_doc );
788     if (!iface) return ;
789 
790 	if (isBreakpoint)
791 	{
792 		iface->addMark( line, Breakpoint );
793 		if (m_pDebugger)
794 			m_pDebugger->setBreakpoint( m_debugFile, line, true );
795 	}
796 	else
797 	{
798 		iface->removeMark( line, Breakpoint );
799 		if (m_pDebugger)
800 			m_pDebugger->setBreakpoint( m_debugFile, line, false );
801 	}
802 #endif // !NO_GPSIM
803 }
804 
805 
debugRun()806 void TextDocument::debugRun()
807 {
808 #ifndef NO_GPSIM
809 	if (m_pDebugger)
810 	{
811 		m_pDebugger->gpsim()->setRunning(true);
812 		slotInitDebugActions();
813 		return;
814 	}
815 
816 	switch ( guessedCodeType() )
817 	{
818 		case ct_unknown:
819 			KMessageBox::sorry( nullptr, i18n("Unknown code type."), i18n("Cannot debug") );
820 			return;
821 
822 		case ct_hex:
823 			KMessageBox::sorry( nullptr, i18n("Cannot debug hex."), i18n("Cannot debug") );
824 			return;
825 
826 		case ct_microbe:
827 			m_bLoadDebuggerAsHLL = true;
828 			m_debugFile = outputFilePath(".microbe");
829 			break;
830 
831 		case ct_asm:
832 			m_bLoadDebuggerAsHLL = false;
833 			m_debugFile = outputFilePath(".asm");
834 			break;
835 
836 		case ct_c:
837 			m_bLoadDebuggerAsHLL = true;
838 			m_debugFile = outputFilePath(".c");
839 			break;
840 	}
841 
842 	m_symbolFile = GpsimProcessor::generateSymbolFile( m_debugFile, this, SLOT(slotCODCreationSucceeded()), SLOT(slotCODCreationFailed()) );
843 #endif // !NO_GPSIM
844 }
845 
846 
debugInterrupt()847 void TextDocument::debugInterrupt()
848 {
849 #ifndef NO_GPSIM
850 	if (!m_pDebugger)
851 		return;
852 
853 	m_pDebugger->gpsim()->setRunning(false);
854 	slotInitDebugActions();
855 #endif // !NO_GPSIM
856 }
857 
858 
debugStop()859 void TextDocument::debugStop()
860 {
861 #ifndef NO_GPSIM
862 	if ( !m_pDebugger || !m_bOwnDebugger )
863 		return;
864 
865 	m_pDebugger->gpsim()->deleteLater();
866 	m_pDebugger = nullptr;
867 	slotDebugSetCurrentLine( SourceLine() );
868 	slotInitDebugActions();
869 #endif // !NO_GPSIM
870 }
871 
872 
debugStep()873 void TextDocument::debugStep()
874 {
875 #ifndef NO_GPSIM
876 	if (!m_pDebugger)
877 		return;
878 
879 	m_pDebugger->stepInto();
880 #endif // !NO_GPSIM
881 }
882 
883 
debugStepOver()884 void TextDocument::debugStepOver()
885 {
886 #ifndef NO_GPSIM
887 	if (!m_pDebugger)
888 		return;
889 
890 	m_pDebugger->stepOver();
891 #endif // !NO_GPSIM
892 }
893 
894 
debugStepOut()895 void TextDocument::debugStepOut()
896 {
897 #ifndef NO_GPSIM
898 	if (!m_pDebugger)
899 		return;
900 
901 	m_pDebugger->stepOut();
902 #endif // !NO_GPSIM
903 }
904 
905 
slotDebugSetCurrentLine(const SourceLine & line)906 void TextDocument::slotDebugSetCurrentLine( const SourceLine & line )
907 {
908 #ifndef NO_GPSIM
909 
910     KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface*>( m_doc );
911     if (!iface) return;
912 
913 	int textLine = line.line();
914 
915 	if ( DocManager::self()->findDocument(QUrl::fromLocalFile(line.fileName())) != this )
916 		textLine = -1;
917 
918 	iface->removeMark( m_lastDebugLineAt, ExecutionPoint );
919 	iface->addMark( textLine, ExecutionPoint );
920 
921 	if ( activeView() )
922 		textView()->setCursorPosition( textLine, 0 );
923 
924 	m_lastDebugLineAt = textLine;
925 #endif // !NO_GPSIM
926 }
927 
928 
slotInitDebugActions()929 void TextDocument::slotInitDebugActions()
930 {
931 #ifndef NO_GPSIM
932 	if ( m_pDebugger )
933 	{
934 		if ( m_pDebugger->gpsim()->isRunning() )
935 			slotDebugSetCurrentLine( SourceLine() );
936 		else
937 			slotDebugSetCurrentLine( m_pDebugger->currentLine() );
938 	}
939 
940 	if ( activeView() )
941 		textView()->slotInitDebugActions();
942 #endif // !NO_GPSIM
943 }
944 
945 
slotCODCreationSucceeded()946 void TextDocument::slotCODCreationSucceeded()
947 {
948 #ifndef NO_GPSIM
949 	GpsimProcessor * gpsim = new GpsimProcessor( m_symbolFile, this );
950 
951 	if (m_bLoadDebuggerAsHLL)
952 		gpsim->setDebugMode( GpsimDebugger::HLLDebugger );
953 	else
954 		gpsim->setDebugMode( GpsimDebugger::AsmDebugger );
955 
956 	setDebugger( gpsim->currentDebugger(), true );
957 #endif // !NO_GPSIM
958 }
slotCODCreationFailed()959 void TextDocument::slotCODCreationFailed()
960 {
961 #ifndef NO_GPSIM
962 	m_debugFile = QString::null;
963 	m_symbolFile = QString::null;
964 #endif // !NO_GPSIM
965 }
966 
967 
slotDebuggerDestroyed()968 void TextDocument::slotDebuggerDestroyed()
969 {
970 #ifndef NO_GPSIM
971 	slotDebugSetCurrentLine( SourceLine() );
972 	m_pDebugger = nullptr;
973 	m_debugFile = QString::null;
974 	slotInitDebugActions();
975 #endif // !NO_GPSIM
976 }
977 
978 
979 #ifndef NO_GPSIM
clearBreakpoints()980 void TextDocument::clearBreakpoints()
981 {
982 	//QPtrList<KTextEditor::Mark> markList = m_doc->marks();
983     //typedef QPtrList<KTextEditor::Mark> MarkList;
984     typedef QHash<int, KTextEditor::Mark*> MarkList;
985     KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface*>( m_doc );
986     if (!iface) return;
987     //MarkList markList = m_doc->marks();
988     MarkList markList = iface->marks(); // note: this will copy
989 
990 	// Find out what marks need adding to our internal lists
991 	//for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() )
992     for (MarkList::iterator itMark = markList.begin(); itMark != markList.end(); ++itMark)
993 	{
994         KTextEditor::Mark * mark = itMark.value();
995 		if ( mark->type & Bookmark )
996 			iface->removeMark( mark->line, Breakpoint );
997 	}
998 
999 	slotUpdateMarksInfo();
1000 }
1001 
1002 
syncBreakpoints()1003 void TextDocument::syncBreakpoints()
1004 {
1005 	if (b_lockSyncBreakpoints)
1006 		return;
1007 
1008 	// We don't really care about synching marks if we aren't debugging / aren't able to take use of the marks
1009 	if (!m_pDebugger)
1010 		return;
1011 
1012 	b_lockSyncBreakpoints = true;
1013 
1014     //QPtrList<KTextEditor::Mark> markList = m_doc->marks();
1015     //typedef QPtrList<KTextEditor::Mark> MarkList;
1016     typedef QHash<int, KTextEditor::Mark*> MarkList;
1017     KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface*>( m_doc );
1018     if (!iface) return;
1019     //MarkList markList = m_doc->marks();
1020     MarkList markList = iface->marks(); // note: this will copy
1021 
1022 	IntList bpList;
1023 
1024 	// Find out what marks need adding to our internal lists
1025 	//for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() )
1026     for (MarkList::iterator itMark = markList.begin(); itMark != markList.end(); ++itMark)
1027 	{
1028         KTextEditor::Mark *mark = itMark.value();
1029 		const int line = mark->line;
1030 
1031 		if ( mark->type & Breakpoint )
1032 			bpList.append(line);
1033 
1034 		if ( mark->type == ExecutionPoint )
1035 			m_lastDebugLineAt = line;
1036 	}
1037 
1038 	m_pDebugger->setBreakpoints( m_debugFile, bpList );
1039 
1040 	b_lockSyncBreakpoints = false;
1041 }
1042 
1043 
debuggerIsRunning() const1044 bool TextDocument::debuggerIsRunning() const
1045 {
1046 	return m_pDebugger;
1047 }
1048 
1049 
debuggerIsStepping() const1050 bool TextDocument::debuggerIsStepping() const
1051 {
1052 	return m_pDebugger && !m_pDebugger->gpsim()->isRunning();
1053 }
1054 
1055 
setDebugger(GpsimDebugger * debugger,bool ownDebugger)1056 void TextDocument::setDebugger( GpsimDebugger * debugger, bool ownDebugger )
1057 {
1058 	if ( debugger == m_pDebugger )
1059 		return;
1060 
1061 	// If we create a gpsim, then we may get called by DebugManager, which will
1062 	// try to claim we don't own it. So if we have a symbol file waiting, thne
1063 	// wait until we are called from its successful creation
1064 	if ( !m_symbolFile.isEmpty() && !ownDebugger )
1065 		return;
1066 
1067 	// Reset it for use next time
1068 	m_symbolFile = QString::null;
1069 
1070 	if (m_bOwnDebugger)
1071 		delete m_pDebugger;
1072 
1073 	m_pDebugger = debugger;
1074 	m_bOwnDebugger = ownDebugger;
1075 
1076 	if (!m_pDebugger)
1077 		return;
1078 
1079 	if ( m_debugFile.isEmpty() )
1080 		m_debugFile = url().path();
1081 
1082 	connect( m_pDebugger,			SIGNAL(destroyed()),						this, SLOT(slotDebuggerDestroyed()) );
1083 	connect( m_pDebugger->gpsim(),	SIGNAL(runningStatusChanged(bool )), 		this, SLOT(slotInitDebugActions()) );
1084 	connect( m_pDebugger,			SIGNAL(lineReached(const SourceLine &)),	this, SLOT(slotDebugSetCurrentLine(const SourceLine &)) );
1085 	m_pDebugger->setBreakpoints( m_debugFile, breakpointList() );
1086 
1087 	slotInitDebugActions();
1088 	if ( !m_pDebugger->gpsim()->isRunning() )
1089 		slotDebugSetCurrentLine( m_pDebugger->currentLine() );
1090 
1091 	if ( this == dynamic_cast<TextDocument*>(DocManager::self()->getFocusedDocument()) )
1092 		SymbolViewer::self()->setContext( m_pDebugger->gpsim() );
1093 }
1094 #endif // !NO_GPSIM
1095 
1096 
inactiveBreakpointPixmap()1097 const QPixmap* TextDocument::inactiveBreakpointPixmap()
1098 {
1099 	const char*breakpoint_gr_xpm[]={
1100 		"11 16 6 1",
1101 		"c c #c6c6c6",
1102 		"d c #2c2c2c",
1103 		"# c #000000",
1104 		". c None",
1105 		"a c #ffffff",
1106 		"b c #555555",
1107 		"...........",
1108 		"...........",
1109 		"...#####...",
1110 		"..#aaaaa#..",
1111 		".#abbbbbb#.",
1112 		"#abbbbbbbb#",
1113 		"#abcacacbd#",
1114 		"#abbbbbbbb#",
1115 		"#abcacacbd#",
1116 		"#abbbbbbbb#",
1117 		".#bbbbbbb#.",
1118 		"..#bdbdb#..",
1119 		"...#####...",
1120 		"...........",
1121 		"...........",
1122 		"..........."};
1123 		static QPixmap pixmap( breakpoint_gr_xpm );
1124 		return &pixmap;
1125 }
1126 
1127 
activeBreakpointPixmap()1128 const QPixmap* TextDocument::activeBreakpointPixmap()
1129 {
1130 	const char* breakpoint_xpm[]={
1131 		"11 16 6 1",
1132 		"c c #c6c6c6",
1133 		". c None",
1134 		"# c #000000",
1135 		"d c #840000",
1136 		"a c #ffffff",
1137 		"b c #ff0000",
1138 		"...........",
1139 		"...........",
1140 		"...#####...",
1141 		"..#aaaaa#..",
1142 		".#abbbbbb#.",
1143 		"#abbbbbbbb#",
1144 		"#abcacacbd#",
1145 		"#abbbbbbbb#",
1146 		"#abcacacbd#",
1147 		"#abbbbbbbb#",
1148 		".#bbbbbbb#.",
1149 		"..#bdbdb#..",
1150 		"...#####...",
1151 		"...........",
1152 		"...........",
1153 		"..........."};
1154 		static QPixmap pixmap( breakpoint_xpm );
1155 		return &pixmap;
1156 }
1157 
1158 
1159 
reachedBreakpointPixmap()1160 const QPixmap* TextDocument::reachedBreakpointPixmap()
1161 {
1162 	const char*breakpoint_bl_xpm[]={
1163 		"11 16 7 1",
1164 		"a c #c0c0ff",
1165 		"# c #000000",
1166 		"c c #0000c0",
1167 		"e c #0000ff",
1168 		"b c #dcdcdc",
1169 		"d c #ffffff",
1170 		". c None",
1171 		"...........",
1172 		"...........",
1173 		"...#####...",
1174 		"..#ababa#..",
1175 		".#bcccccc#.",
1176 		"#acccccccc#",
1177 		"#bcadadace#",
1178 		"#acccccccc#",
1179 		"#bcadadace#",
1180 		"#acccccccc#",
1181 		".#ccccccc#.",
1182 		"..#cecec#..",
1183 		"...#####...",
1184 		"...........",
1185 		"...........",
1186 		"..........."};
1187 		static QPixmap pixmap( breakpoint_bl_xpm );
1188 		return &pixmap;
1189 }
1190 
1191 
disabledBreakpointPixmap()1192 const QPixmap* TextDocument::disabledBreakpointPixmap()
1193 {
1194 	const char*breakpoint_wh_xpm[]={
1195 		"11 16 7 1",
1196 		"a c #c0c0ff",
1197 		"# c #000000",
1198 		"c c #0000c0",
1199 		"e c #0000ff",
1200 		"b c #dcdcdc",
1201 		"d c #ffffff",
1202 		". c None",
1203 		"...........",
1204 		"...........",
1205 		"...#####...",
1206 		"..#ddddd#..",
1207 		".#ddddddd#.",
1208 		"#ddddddddd#",
1209 		"#ddddddddd#",
1210 		"#ddddddddd#",
1211 		"#ddddddddd#",
1212 		"#ddddddddd#",
1213 		".#ddddddd#.",
1214 		"..#ddddd#..",
1215 		"...#####...",
1216 		"...........",
1217 		"...........",
1218 		"..........."};
1219 		static QPixmap pixmap( breakpoint_wh_xpm );
1220 		return &pixmap;
1221 }
1222 
1223 
executionPointPixmap()1224 const QPixmap* TextDocument::executionPointPixmap()
1225 {
1226 	const char*exec_xpm[]={
1227 		"11 16 4 1",
1228 		"a c #00ff00",
1229 		"b c #000000",
1230 		". c None",
1231 		"# c #00c000",
1232 		"...........",
1233 		"...........",
1234 		"...........",
1235 		"#a.........",
1236 		"#aaa.......",
1237 		"#aaaaa.....",
1238 		"#aaaaaaa...",
1239 		"#aaaaaaaaa.",
1240 		"#aaaaaaa#b.",
1241 		"#aaaaa#b...",
1242 		"#aaa#b.....",
1243 		"#a#b.......",
1244 		"#b.........",
1245 		"...........",
1246 		"...........",
1247 		"..........."};
1248 		static QPixmap pixmap( exec_xpm );
1249 		return &pixmap;
1250 }
1251