File indexing completed on 2024-09-08 08:10:30
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 <KXMLGUIClient> 0012 0013 #include "asmformatter.h" 0014 #include "config.h" 0015 #include "filemetainfo.h" 0016 #include "gpsimprocessor.h" 0017 #include "ktechlab.h" 0018 #include "symbolviewer.h" 0019 #include "textdocument.h" 0020 #include "textview.h" 0021 #include "variablelabel.h" 0022 #include "viewiface.h" 0023 0024 //#include <ktexteditor/editinterface.h> // ? 0025 #include <KTextEditor/TextHintInterface> 0026 0027 // #include "kateview.h" 0028 #include <KActionCollection> 0029 #include <KLocalizedString> 0030 // #include <k3popupmenu.h> 0031 #include <KToolBarPopupAction> 0032 #include <KXMLGUIFactory> 0033 0034 #include <QActionGroup> 0035 #include <QApplication> 0036 #include <QCursor> 0037 #include <QVBoxLayout> 0038 //#include <qobjectlist.h> 0039 #include <QClipboard> 0040 #include <QFocusEvent> 0041 #include <QMenu> 0042 #include <QStandardPaths> 0043 #include <QTimer> 0044 0045 #include <ktechlab_debug.h> 0046 0047 // BEGIN class TextView 0048 TextView::TextView(TextDocument *textDocument, ViewContainer *viewContainer, uint viewAreaId) 0049 : View(textDocument, viewContainer, viewAreaId) 0050 { 0051 m_view = textDocument->createKateView(this); 0052 m_view->insertChildClient(this); 0053 0054 KActionCollection *ac = actionCollection(); 0055 0056 // BEGIN Convert To * Actions 0057 // KToolBarPopupAction * pa = new KToolBarPopupAction( i18n("Convert to"), "fork", 0, 0, 0, ac, "program_convert" ); 0058 KToolBarPopupAction *pa = new KToolBarPopupAction(QIcon::fromTheme("fork"), i18n("Convert To"), ac); 0059 pa->setObjectName("program_convert"); 0060 pa->setDelayed(false); 0061 ac->addAction(pa->objectName(), pa); 0062 0063 QMenu *m = pa->menu(); 0064 0065 m->setTitle(i18n("Convert To")); 0066 QAction *actToMicrobe = m->addAction(QIcon::fromTheme("convert_to_microbe"), i18n("Microbe")); 0067 actToMicrobe->setData(TextDocument::MicrobeOutput); 0068 m->addAction(QIcon::fromTheme("convert_to_assembly"), i18n("Assembly"))->setData(TextDocument::AssemblyOutput); 0069 m->addAction(QIcon::fromTheme("convert_to_hex"), i18n("Hex"))->setData(TextDocument::HexOutput); 0070 m->addAction(QIcon::fromTheme("convert_to_pic"), i18n("PIC (upload)"))->setData(TextDocument::PICOutput); 0071 connect(m, &QMenu::triggered, textDocument, &TextDocument::slotConvertTo); 0072 0073 // m->setItemEnabled( TextDocument::MicrobeOutput, false ); // 2018.12.02 0074 actToMicrobe->setEnabled(false); 0075 ac->addAction(pa->objectName(), pa); 0076 // END Convert To * Actions 0077 0078 { 0079 // new QAction( i18n("Format Assembly Code"), "", Qt::Key_F12, textDocument, SLOT(formatAssembly()), ac, "format_asm" ); 0080 QAction *action = new QAction(i18n("Format Assembly Code"), ac); 0081 action->setObjectName("format_asm"); 0082 action->setShortcut(Qt::Key_F12); 0083 connect(action, &QAction::triggered, textDocument, &TextDocument::formatAssembly); 0084 ac->addAction(action->objectName(), action); 0085 } 0086 0087 #ifndef NO_GPSIM 0088 // BEGIN Debug Actions 0089 { 0090 // new QAction( i18n("Set &Breakpoint"), 0, 0, this, SLOT(toggleBreakpoint()), ac, "debug_toggle_breakpoint" ); 0091 QAction *action = new QAction(i18n("Set &Breakpoint"), ac); 0092 action->setObjectName("debug_toggle_breakpoint"); 0093 connect(action, &QAction::triggered, this, &TextView::toggleBreakpoint); 0094 ac->addAction(action->objectName(), action); 0095 } 0096 { 0097 // new QAction( i18n("Run"), "debug-run", 0, textDocument, SLOT(debugRun()), ac, "debug_run" ); 0098 QAction *action = new QAction(QIcon::fromTheme("debug-run"), i18n("Run"), ac); 0099 action->setObjectName("debug_run"); 0100 connect(action, &QAction::triggered, textDocument, &TextDocument::debugRun); 0101 ac->addAction(action->objectName(), action); 0102 } 0103 { 0104 // new QAction( i18n("Interrupt"), "media-playback-pause", 0, textDocument, SLOT(debugInterrupt()), ac, "debug_interrupt" ); 0105 QAction *action = new QAction(QIcon::fromTheme("media-playback-pause"), i18n("Interrupt"), ac); 0106 action->setObjectName("debug_interrupt"); 0107 connect(action, &QAction::triggered, textDocument, &TextDocument::debugInterrupt); 0108 ac->addAction(action->objectName(), action); 0109 } 0110 { 0111 // new QAction( i18n("Stop"), "process-stop", 0, textDocument, SLOT(debugStop()), ac, "debug_stop" ); 0112 QAction *action = new QAction(QIcon::fromTheme("process-stop"), i18n("Stop"), ac); 0113 action->setObjectName("debug_stop"); 0114 connect(action, &QAction::triggered, textDocument, &TextDocument::debugStop); 0115 ac->addAction(action->objectName(), action); 0116 } 0117 { 0118 // new QAction( i18n("Step"), "debug-step-instruction", Qt::CTRL|Qt::ALT|Qt::Key_Right, textDocument, SLOT(debugStep()), ac, "debug_step" ); 0119 QAction *action = new QAction(QIcon::fromTheme("debug-step-instruction"), i18n("Step"), ac); 0120 action->setObjectName("debug_step"); 0121 action->setShortcut(Qt::CTRL | Qt::ALT | Qt::Key_Right); 0122 connect(action, &QAction::triggered, textDocument, &TextDocument::debugStep); 0123 ac->addAction(action->objectName(), action); 0124 } 0125 { 0126 // new QAction( i18n("Step Over"), "debug-step-over", 0, textDocument, SLOT(debugStepOver()), ac, "debug_step_over" ); 0127 QAction *action = new QAction(QIcon::fromTheme("debug-step-over"), i18n("Step Over"), ac); 0128 action->setObjectName("debug_step_over"); 0129 connect(action, &QAction::triggered, textDocument, &TextDocument::debugStepOver); 0130 ac->addAction(action->objectName(), action); 0131 } 0132 { 0133 // new QAction( i18n("Step Out"), "debug-step-out", 0, textDocument, SLOT(debugStepOut()), ac, "debug_step_out" ); 0134 QAction *action = new QAction(QIcon::fromTheme("debug-step-out"), i18n("Step Out"), ac); 0135 action->setObjectName("debug_step_out"); 0136 connect(action, &QAction::triggered, textDocument, &TextDocument::debugStepOut); 0137 ac->addAction(action->objectName(), action); 0138 } 0139 // END Debug Actions 0140 #endif 0141 0142 setXMLFile("ktechlabtextui.rc"); 0143 0144 // m_view->setXMLFile("ktechlabkateui.rc") is protected, replace it with code below 0145 { 0146 // see https://github.com/KDE/kxmlgui/blob/master/src/kxmlguiclient.cpp#L219 0147 QString _file = "ktechlabkateui.rc"; 0148 QStringList allFiles; 0149 0150 const QString filter = componentName() + QLatin1Char('/') + _file; 0151 0152 // files on filesystem 0153 allFiles << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("kxmlgui5/") + filter); // KF >= 5.1 0154 0155 // KF >= 5.4 (resource file) 0156 const QString qrcFile(QLatin1String(":/kxmlgui5/") + filter); 0157 if (QFile::exists(qrcFile)) { 0158 allFiles << qrcFile; 0159 } 0160 0161 // then compat locations 0162 const QStringList compatFiles = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, filter) + // kdelibs4, KF 5.0 0163 QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, _file); // kdelibs4, KF 5.0, caller passes component name 0164 0165 if (allFiles.isEmpty() && !compatFiles.isEmpty()) { 0166 qCWarning(KTL_LOG) << "KXMLGUI file found at deprecated location" << compatFiles 0167 << "-- please use ${KDE_INSTALL_KXMLGUI5DIR} to install this file instead."; 0168 } 0169 allFiles += compatFiles; 0170 0171 QString doc; 0172 if (!allFiles.isEmpty()) { 0173 QString fileFound = findMostRecentXMLFile(allFiles, doc); 0174 qCInfo(KTL_LOG) << "KXMLGUI file found at : " << fileFound; 0175 m_view->replaceXMLFile(fileFound, QString(), false); 0176 } else { 0177 qCWarning(KTL_LOG) << "KXMLGUI not found for " << _file; 0178 } 0179 } 0180 0181 m_savedCursorLine = 0; 0182 m_savedCursorColumn = 0; 0183 m_pViewIface = new TextViewIface(this); 0184 0185 setAcceptDrops(true); 0186 0187 // m_view->installPopup( static_cast<Q3PopupMenu*>( KTechlab::self()->factory()->container( "ktexteditor_popup", KTechlab::self() ) ) ); 0188 m_view->setContextMenu(static_cast<QMenu *>(KTechlab::self()->factory()->container("ktexteditor_popup", KTechlab::self()))); 0189 0190 // TODO this ought to not use internal widgets of the ktexteditor view, 0191 // and only use KTextEditor::TextHintInterface for the hint instead 0192 // of the own popup logic 0193 // QWidget *internalView = m_view->findChild<QWidget *>("KateViewInternal"); 0194 0195 connect(m_view, &KTextEditor::View::cursorPositionChanged, this, &TextView::slotCursorPositionChanged); 0196 connect(m_view, &KTextEditor::View::selectionChanged, this, &TextView::slotSelectionmChanged); 0197 0198 // setFocusWidget(internalView); 0199 connect(this, &TextView::focused, this, &TextView::gotFocus); 0200 0201 m_layout->insertWidget(0, m_view); 0202 0203 slotCursorPositionChanged(); 0204 slotInitDebugActions(); 0205 initCodeActions(); 0206 0207 #ifndef NO_GPSIM 0208 m_pTextViewLabel = new VariableLabel(this); 0209 m_pTextViewLabel->hide(); 0210 0211 TextViewEventFilter *eventFilter = new TextViewEventFilter(this); 0212 connect(eventFilter, &TextViewEventFilter::wordHoveredOver, this, &TextView::slotWordHoveredOver); 0213 connect(eventFilter, &TextViewEventFilter::wordUnhovered, this, &TextView::slotWordUnhovered); 0214 0215 // internalView->installEventFilter(eventFilter); 0216 #endif 0217 0218 // TODO HACK disable some actions which collide with ktechlab's actions. 0219 // the proper solution would be to move the actions from KTechLab object level to document level for 0220 // all types of documents 0221 for (QAction *act : actionCollection()->actions()) { 0222 qCDebug(KTL_LOG) << "act: " << act->text() << " shortcut " << act->shortcut() << ":" << act; 0223 0224 if (((act->objectName()) == QLatin1String("file_save")) || ((act->objectName()) == QLatin1String("file_save_as")) || ((act->objectName()) == QLatin1String("file_print")) || ((act->objectName()) == QLatin1String("edit_undo")) || 0225 ((act->objectName()) == QLatin1String("edit_redo")) || ((act->objectName()) == QLatin1String("edit_cut")) || ((act->objectName()) == QLatin1String("edit_copy")) || ((act->objectName()) == QLatin1String("edit_paste"))) { 0226 act->setShortcutContext(Qt::WidgetWithChildrenShortcut); 0227 // act->setShortcutConfigurable(true); 0228 act->setShortcut(Qt::Key_unknown); 0229 qCDebug(KTL_LOG) << "action " << act << " disabled"; 0230 } 0231 } 0232 } 0233 0234 TextView::~TextView() 0235 { 0236 if (KTechlab::self()) { 0237 // 2017.01.09: do not crash on document close. factory has its clients removed in TextDocument::~TextDocument() 0238 // if ( KXMLGUIFactory * f = m_view->factory() ) 0239 // f->removeClient( m_view ); 0240 KTechlab::self()->addNoRemoveGUIClient(m_view); 0241 } 0242 0243 delete m_pViewIface; 0244 } 0245 0246 bool TextView::closeView() 0247 { 0248 if (textDocument()) { 0249 const QUrl url = textDocument()->url(); 0250 if (!url.isEmpty()) 0251 fileMetaInfo()->grabMetaInfo(url, this); 0252 } 0253 0254 bool doClose = View::closeView(); 0255 if (doClose) 0256 KTechlab::self()->factory()->removeClient(m_view); 0257 return View::closeView(); 0258 } 0259 0260 bool TextView::gotoLine(const int line) 0261 { 0262 // return m_view->setCursorPosition( line, 0/*m_view->cursorColumn()*/ ); 0263 return m_view->setCursorPosition(KTextEditor::Cursor(line, 0 /*m_view->cursorColumn()*/)); 0264 } 0265 0266 TextDocument *TextView::textDocument() const 0267 { 0268 return static_cast<TextDocument *>(document()); 0269 } 0270 void TextView::undo() 0271 { 0272 qCDebug(KTL_LOG); 0273 // note: quite a hack, but could not find any more decent way of getting to undo/redo interface 0274 // note: quite a hack, but could not find any more decent way of getting to undo/redo interface 0275 QAction *action = actionByName("edit_undo"); 0276 if (action) { 0277 action->trigger(); 0278 return; 0279 } 0280 qCWarning(KTL_LOG) << "no edit_undo action in text view! no action taken"; 0281 } 0282 void TextView::redo() 0283 { 0284 qCDebug(KTL_LOG); 0285 // note: quite a hack, but could not find any more decent way of getting to undo/redo interface 0286 QAction *action = actionByName("edit_redo"); 0287 if (action) { 0288 action->trigger(); 0289 return; 0290 } 0291 qCWarning(KTL_LOG) << "no edit_redo action in text view! no action taken"; 0292 } 0293 0294 void TextView::cut() 0295 { 0296 // m_view-> cut(); 0297 if (!m_view->selection()) 0298 return; 0299 QClipboard *clipboard = QApplication::clipboard(); 0300 clipboard->setText(m_view->document()->text(m_view->selectionRange())); 0301 m_view->document()->removeText(m_view->selectionRange()); 0302 } 0303 0304 void TextView::copy() 0305 { 0306 // m_view->copy(); 0307 if (!m_view->selection()) 0308 return; 0309 QClipboard *clipboard = QApplication::clipboard(); 0310 clipboard->setText(m_view->document()->text(m_view->selectionRange())); 0311 } 0312 0313 void TextView::paste() 0314 { 0315 // m_view->paste(); 0316 QClipboard *clipboard = QApplication::clipboard(); 0317 m_view->document()->insertText(m_view->cursorPosition(), clipboard->text()); 0318 } 0319 0320 void TextView::disableActions() 0321 { 0322 QMenu *tb = (dynamic_cast<KToolBarPopupAction *>(actionByName("program_convert")))->menu(); 0323 0324 const QList<QAction *> actions = tb->actions(); 0325 for (QAction *a : actions) { 0326 switch (a->data().toInt()) { 0327 case TextDocument::AssemblyOutput: 0328 case TextDocument::HexOutput: 0329 case TextDocument::PICOutput: 0330 a->setEnabled(false); 0331 break; 0332 default: 0333 qCDebug(KTL_LOG) << " skip action: " << a; 0334 } 0335 } 0336 // tb->setItemEnabled( TextDocument::AssemblyOutput, false ); // 2018.12.02 0337 // tb->setItemEnabled( TextDocument::HexOutput, false ); 0338 // tb->setItemEnabled( TextDocument::PICOutput, false ); 0339 actionByName("format_asm")->setEnabled(false); 0340 0341 #ifndef NO_GPSIM 0342 actionByName("debug_toggle_breakpoint")->setEnabled(false); 0343 #endif 0344 } 0345 0346 // KTextEditor::View::saveResult TextView::save() { return m_view->save(); } 0347 bool TextView::save() 0348 { 0349 return (m_view->document()->documentSave()); 0350 } 0351 0352 // KTextEditor::View::saveResult TextView::saveAs() { return m_view->saveAs(); } 0353 bool TextView::saveAs() 0354 { 0355 return m_view->document()->documentSaveAs(); 0356 } 0357 void TextView::print() 0358 { 0359 qCDebug(KTL_LOG); 0360 // note: quite a hack, but could not find any more decent way of getting to undo/redo interface 0361 QAction *action = actionByName("file_print"); 0362 if (action) { 0363 action->trigger(); 0364 return; 0365 } 0366 qCWarning(KTL_LOG) << "no file_print action in text view! no action taken"; 0367 } 0368 0369 void TextView::gotFocus() 0370 { 0371 #ifndef NO_GPSIM 0372 GpsimDebugger *debugger = textDocument()->debugger(); 0373 if (!debugger || !debugger->gpsim()) 0374 return; 0375 0376 SymbolViewer::self()->setContext(debugger->gpsim()); 0377 #endif 0378 } 0379 0380 void TextView::slotSelectionmChanged() 0381 { 0382 KTechlab::self()->actionByName("edit_cut")->setEnabled(m_view->selection()); 0383 KTechlab::self()->actionByName("edit_copy")->setEnabled(m_view->selection()); 0384 } 0385 0386 void TextView::initCodeActions() 0387 { 0388 disableActions(); 0389 0390 QMenu *tb = (dynamic_cast<KToolBarPopupAction *>(actionByName("program_convert")))->menu(); 0391 0392 QAction *actHexOut = nullptr; 0393 QAction *actPicOut = nullptr; 0394 QAction *actAsmOut = nullptr; 0395 const QList<QAction *> actions = tb->actions(); 0396 for (QAction *a : actions) { 0397 switch (a->data().toInt()) { 0398 case TextDocument::AssemblyOutput: 0399 actAsmOut = a; 0400 break; 0401 case TextDocument::HexOutput: 0402 actHexOut = a; 0403 break; 0404 case TextDocument::PICOutput: 0405 actPicOut = a; 0406 break; 0407 default: 0408 qCDebug(KTL_LOG) << " skip action: " << a; 0409 } 0410 } 0411 0412 switch (textDocument()->guessedCodeType()) { 0413 case TextDocument::ct_asm: { 0414 // tb->setItemEnabled( TextDocument::HexOutput, true ); // 2018.12.02 0415 // tb->setItemEnabled( TextDocument::PICOutput, true ); 0416 actHexOut->setEnabled(true); 0417 actPicOut->setEnabled(true); 0418 actionByName("format_asm")->setEnabled(true); 0419 #ifndef NO_GPSIM 0420 actionByName("debug_toggle_breakpoint")->setEnabled(true); 0421 slotInitDebugActions(); 0422 #endif 0423 break; 0424 } 0425 case TextDocument::ct_c: { 0426 // tb->setItemEnabled( TextDocument::AssemblyOutput, true ); 0427 // tb->setItemEnabled( TextDocument::HexOutput, true ); 0428 // tb->setItemEnabled( TextDocument::PICOutput, true ); 0429 actAsmOut->setEnabled(true); 0430 actHexOut->setEnabled(true); 0431 actPicOut->setEnabled(true); 0432 break; 0433 } 0434 case TextDocument::ct_hex: { 0435 // tb->setItemEnabled( TextDocument::AssemblyOutput, true ); 0436 // tb->setItemEnabled( TextDocument::PICOutput, true ); 0437 actAsmOut->setEnabled(true); 0438 actPicOut->setEnabled(true); 0439 break; 0440 } 0441 case TextDocument::ct_microbe: { 0442 // tb->setItemEnabled( TextDocument::AssemblyOutput, true ); 0443 // tb->setItemEnabled( TextDocument::HexOutput, true ); 0444 // tb->setItemEnabled( TextDocument::PICOutput, true ); 0445 actAsmOut->setEnabled(true); 0446 actHexOut->setEnabled(true); 0447 actPicOut->setEnabled(true); 0448 break; 0449 } 0450 case TextDocument::ct_unknown: { 0451 break; 0452 } 0453 } 0454 } 0455 0456 void TextView::setCursorPosition(uint line, uint col) 0457 { 0458 // m_view->setCursorPosition( line, col ); 0459 m_view->setCursorPosition(KTextEditor::Cursor(line, col)); 0460 } 0461 0462 unsigned TextView::currentLine() 0463 { 0464 // unsigned l,c ; 0465 KTextEditor::Cursor curs = m_view->cursorPosition(); 0466 return curs.line(); 0467 } 0468 unsigned TextView::currentColumn() 0469 { 0470 // unsigned l,c ; 0471 KTextEditor::Cursor curs = m_view->cursorPosition(); // &l, &c ); 0472 return curs.column(); 0473 } 0474 0475 void TextView::saveCursorPosition() 0476 { 0477 KTextEditor::Cursor curs = m_view->cursorPosition(); // &m_savedCursorLine, &m_savedCursorColumn ); 0478 m_savedCursorLine = curs.line(); 0479 m_savedCursorColumn = curs.column(); 0480 } 0481 0482 void TextView::restoreCursorPosition() 0483 { 0484 m_view->setCursorPosition(KTextEditor::Cursor(m_savedCursorLine, m_savedCursorColumn)); 0485 } 0486 0487 void TextView::slotCursorPositionChanged() 0488 { 0489 uint line, column; 0490 KTextEditor::Cursor curs = m_view->cursorPosition(); //&line, &column ); 0491 line = curs.line(); 0492 column = curs.column(); 0493 0494 m_statusBar->setStatusText(i18n(" Line: %1 Col: %2 ", QString::number(line + 1), QString::number(column + 1))); 0495 0496 slotUpdateMarksInfo(); 0497 } 0498 0499 void TextView::slotUpdateMarksInfo() 0500 { 0501 #ifndef NO_GPSIM 0502 uint l; 0503 //int c; 0504 KTextEditor::Cursor curs = m_view->cursorPosition(); // &l, &c ); 0505 l = curs.line(); 0506 //c = curs.column(); 0507 0508 KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface *>(m_view->document()); 0509 // if ( m_view->getDoc()->mark(l) & TextDocument::Breakpoint ) 0510 if (iface->mark(l) & TextDocument::Breakpoint) 0511 actionByName("debug_toggle_breakpoint")->setText(i18n("Clear &Breakpoint")); 0512 else 0513 actionByName("debug_toggle_breakpoint")->setText(i18n("Set &Breakpoint")); 0514 #endif 0515 } 0516 0517 void TextView::slotInitDebugActions() 0518 { 0519 #ifndef NO_GPSIM 0520 bool isRunning = textDocument()->debuggerIsRunning(); 0521 bool isStepping = textDocument()->debuggerIsStepping(); 0522 bool ownDebugger = textDocument()->ownDebugger(); 0523 0524 actionByName("debug_run")->setEnabled(!isRunning || isStepping); 0525 actionByName("debug_interrupt")->setEnabled(isRunning && !isStepping); 0526 actionByName("debug_stop")->setEnabled(isRunning && ownDebugger); 0527 actionByName("debug_step")->setEnabled(isRunning && isStepping); 0528 actionByName("debug_step_over")->setEnabled(isRunning && isStepping); 0529 actionByName("debug_step_out")->setEnabled(isRunning && isStepping); 0530 #endif // !NO_GPSIM 0531 } 0532 0533 void TextView::toggleBreakpoint() 0534 { 0535 #ifndef NO_GPSIM 0536 uint l; 0537 //int c; 0538 KTextEditor::Cursor curs = m_view->cursorPosition(); // &l, &c ); 0539 l = curs.line(); 0540 //c = curs.column(); 0541 // const bool isBreakpoint = m_view->getDoc()->mark(l) & TextDocument::Breakpoint; 0542 KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface *>(m_view->document()); 0543 if (!iface) 0544 return; 0545 const bool isBreakpoint = iface->mark(l) & TextDocument::Breakpoint; 0546 // textDocument()->setBreakpoint( l, !(m_view->getDoc()->mark(l) & TextDocument::Breakpoint) ); 0547 textDocument()->setBreakpoint(l, !isBreakpoint); 0548 #endif // !NO_GPSIM 0549 } 0550 0551 void TextView::slotWordHoveredOver(const QString &word, int line, int /*col*/) 0552 { 0553 #ifndef NO_GPSIM 0554 // We're only interested in popping something up if we currently have a debugger running 0555 GpsimProcessor *gpsim = textDocument()->debugger() ? textDocument()->debugger()->gpsim() : nullptr; 0556 if (!gpsim) { 0557 m_pTextViewLabel->hide(); 0558 return; 0559 } 0560 0561 // Find out if the word that we are hovering over is the operand data 0562 // KTextEditor::EditInterface * e = (KTextEditor::EditInterface*)textDocument()->kateDocument()->qt_cast("KTextEditor::EditInterface"); 0563 // InstructionParts parts( e->textLine( unsigned(line) ) ); 0564 InstructionParts parts(textDocument()->kateDocument()->line(line)); 0565 if (!parts.operandData().contains(word)) 0566 return; 0567 0568 if (RegisterInfo *info = gpsim->registerMemory()->fromName(word)) 0569 m_pTextViewLabel->setRegister(info, info->name()); 0570 0571 else { 0572 int operandAddress = textDocument()->debugger()->programAddress(textDocument()->debugFile(), line); 0573 if (operandAddress == -1) { 0574 m_pTextViewLabel->hide(); 0575 return; 0576 } 0577 0578 int regAddress = gpsim->operandRegister(operandAddress); 0579 0580 if (regAddress != -1) 0581 m_pTextViewLabel->setRegister(gpsim->registerMemory()->fromAddress(regAddress), word); 0582 0583 else { 0584 m_pTextViewLabel->hide(); 0585 return; 0586 } 0587 } 0588 0589 m_pTextViewLabel->move(mapFromGlobal(QCursor::pos()) + QPoint(0, 20)); 0590 m_pTextViewLabel->show(); 0591 #else 0592 Q_UNUSED(word); 0593 Q_UNUSED(line); 0594 #endif // !NO_GPSIM 0595 } 0596 0597 void TextView::slotWordUnhovered() 0598 { 0599 #ifndef NO_GPSIM 0600 m_pTextViewLabel->hide(); 0601 #endif // !NO_GPSIM 0602 } 0603 // END class TextView 0604 0605 // BEGIN class TextViewEventFilter 0606 TextViewEventFilter::TextViewEventFilter(TextView *textView) 0607 { 0608 m_hoverStatus = Sleeping; 0609 m_pTextView = textView; 0610 m_lastLine = m_lastCol = -1; 0611 0612 //((KTextEditor::TextHintInterface*)textView->kateView()->qt_cast("KTextEditor::TextHintInterface"))->enableTextHints(0); 0613 { 0614 KTextEditor::View *view = textView->kateView(); 0615 KTextEditor::TextHintInterface *iface = qobject_cast<KTextEditor::TextHintInterface *>(view); 0616 if (iface) { 0617 // iface->enableTextHints(0); 0618 iface->registerTextHintProvider(this); 0619 // connect( textView->kateView(), SIGNAL(needTextHint(int, int, QString &)), this, SLOT(slotNeedTextHint( int, int, QString& )) ); 0620 // 2020.09.10 - no such signal 0621 // connect( view, SIGNAL(needTextHint(const KTextEditor::Cursor &, QString &)), 0622 // this, SLOT(slotNeedTextHint(const KTextEditor::Cursor &, QString &)) ); 0623 } else { 0624 qCWarning(KTL_LOG) << "KTextEditor::View does not implement TextHintInterface for " << view; 0625 } 0626 } 0627 0628 m_pHoverTimer = new QTimer(this); 0629 connect(m_pHoverTimer, &QTimer::timeout, this, &TextViewEventFilter::hoverTimeout); 0630 0631 m_pSleepTimer = new QTimer(this); 0632 connect(m_pSleepTimer, &QTimer::timeout, this, &TextViewEventFilter::gotoSleep); 0633 0634 m_pNoWordTimer = new QTimer(this); 0635 connect(m_pNoWordTimer, &QTimer::timeout, this, &TextViewEventFilter::slotNoWordTimeout); 0636 } 0637 TextViewEventFilter::~TextViewEventFilter() 0638 { 0639 KTextEditor::View *view = m_pTextView->kateView(); 0640 KTextEditor::TextHintInterface *iface = qobject_cast<KTextEditor::TextHintInterface *>(view); 0641 if (iface) { 0642 iface->unregisterTextHintProvider(this); 0643 } 0644 } 0645 0646 QString TextViewEventFilter::textHint(KTextEditor::View * /*view*/, const KTextEditor::Cursor &position) 0647 { 0648 qCDebug(KTL_LOG) << "TextViewEventFilter::textHint: position=" << position.toString(); 0649 QString str; 0650 slotNeedTextHint(position, str); 0651 return QString(); 0652 } 0653 0654 bool TextViewEventFilter::eventFilter(QObject *, QEvent *e) 0655 { 0656 // qCDebug(KTL_LOG) << "e->type() = " << e->type(); 0657 0658 if (e->type() == QEvent::MouseMove) { 0659 if (!m_pNoWordTimer->isActive()) 0660 m_pNoWordTimer->start(10); 0661 return false; 0662 } 0663 0664 if (e->type() == QEvent::FocusOut || e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress || e->type() == QEvent::Leave || e->type() == QEvent::Wheel) { 0665 // user moved focus somewhere - hide the tip and sleep 0666 if ((static_cast<QFocusEvent *>(e))->reason() != Qt::PopupFocusReason) 0667 updateHovering(nullptr, -1, -1); 0668 } 0669 0670 return false; 0671 } 0672 0673 void TextViewEventFilter::hoverTimeout() 0674 { 0675 m_pSleepTimer->stop(); 0676 m_hoverStatus = Active; 0677 emit wordHoveredOver(m_lastWord, m_lastLine, m_lastCol); 0678 } 0679 0680 void TextViewEventFilter::gotoSleep() 0681 { 0682 m_hoverStatus = Sleeping; 0683 m_lastWord = QString(); 0684 emit wordUnhovered(); 0685 m_pHoverTimer->stop(); 0686 } 0687 0688 void TextViewEventFilter::slotNoWordTimeout() 0689 { 0690 updateHovering(nullptr, -1, -1); 0691 } 0692 0693 void TextViewEventFilter::updateHovering(const QString ¤tWord, int line, int col) 0694 { 0695 if ((currentWord == m_lastWord) && (line == m_lastLine)) 0696 return; 0697 0698 m_lastWord = currentWord; 0699 m_lastLine = line; 0700 m_lastCol = col; 0701 0702 if (currentWord.isEmpty()) { 0703 if (m_hoverStatus == Active) 0704 m_hoverStatus = Hidden; 0705 0706 emit wordUnhovered(); 0707 if (!m_pSleepTimer->isActive()) { 0708 m_pSleepTimer->setSingleShot(true); 0709 m_pSleepTimer->start(2000 /*, true */); 0710 } 0711 return; 0712 } 0713 0714 if (m_hoverStatus != Sleeping) { 0715 emit wordHoveredOver(currentWord, line, col); 0716 } else { 0717 m_pHoverTimer->setSingleShot(true); 0718 m_pHoverTimer->start(700 /*, true */); 0719 } 0720 } 0721 0722 static inline bool isWordLetter(const QString &s) 0723 { 0724 return (s.length() == 1) && (s[0].isLetterOrNumber() || s[0] == '_'); 0725 } 0726 0727 void TextViewEventFilter::slotNeedTextHint(const KTextEditor::Cursor &position, QString & /*text*/) 0728 { 0729 int line = position.line(); 0730 int col = position.column(); 0731 m_pNoWordTimer->stop(); 0732 0733 // KTextEditor::EditInterface * e = (KTextEditor::EditInterface*)m_pTextView->textDocument()->kateDocument()->qt_cast("KTextEditor::EditInterface"); 0734 KTextEditor::Document *d = m_pTextView->textDocument()->kateDocument(); 0735 0736 // Return if we aren't currently in a word 0737 if (!isWordLetter(d->text(KTextEditor::Range(line, col, line, col + 1)))) { 0738 updateHovering(QString(), line, col); 0739 return; 0740 } 0741 0742 // Find the start of the word 0743 int wordStart = col; 0744 do 0745 wordStart--; 0746 while (wordStart > 0 && isWordLetter(d->text(KTextEditor::Range(line, wordStart, line, wordStart + 1)))); 0747 wordStart++; 0748 0749 // Find the end of the word 0750 int wordEnd = col; 0751 do 0752 wordEnd++; 0753 while (isWordLetter(d->text(KTextEditor::Range(line, wordEnd, line, wordEnd + 1)))); 0754 0755 QString t = d->text(KTextEditor::Range(line, wordStart, line, wordEnd)); 0756 0757 updateHovering(t, line, col); 0758 } 0759 // END class TextViewEventFilter 0760 0761 #include "moc_textview.cpp"