File indexing completed on 2024-05-12 04:37:35
0001 /* 0002 SPDX-FileCopyrightText: 2002 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> 0003 SPDX-FileCopyrightText: 2002 John Firebaugh <jfirebaugh@kde.org> 0004 SPDX-FileCopyrightText: 2006, 2008 Vladimir Prus <ghost@cs.msu.su> 0005 SPDX-FileCopyrightText: 2007 Hamish Rodda <rodda@kde.org> 0006 SPDX-FileCopyrightText: 2009 Niko Sams <niko.sams@gmail.com> 0007 0008 SPDX-License-Identifier: LGPL-2.0-or-later 0009 */ 0010 0011 #include "breakpointmodel.h" 0012 0013 #include <QIcon> 0014 #include <QPixmap> 0015 #include <QTimer> 0016 0017 #include <KLocalizedString> 0018 #include <KTextEditor/Document> 0019 #include <KTextEditor/MovingInterface> 0020 0021 #include "../interfaces/icore.h" 0022 #include "../interfaces/idebugcontroller.h" 0023 #include "../interfaces/idocumentcontroller.h" 0024 #include "../interfaces/idocument.h" 0025 #include "../interfaces/ipartcontroller.h" 0026 #include <interfaces/idebugsession.h> 0027 #include <interfaces/ibreakpointcontroller.h> 0028 #include <interfaces/isession.h> 0029 #include <debug.h> 0030 #include "breakpoint.h" 0031 #include <KConfigGroup> 0032 #include <QAction> 0033 #include <QMenu> 0034 #include <QMessageBox> 0035 0036 #define IF_DEBUG(x) 0037 0038 using namespace KDevelop; 0039 using namespace KTextEditor; 0040 0041 namespace { 0042 0043 IBreakpointController* breakpointController() 0044 { 0045 KDevelop::ICore* core = KDevelop::ICore::self(); 0046 if (!core) { 0047 return nullptr; 0048 } 0049 IDebugController* controller = core->debugController(); 0050 if (!controller) { 0051 return nullptr; 0052 } 0053 IDebugSession* session = controller->currentSession(); 0054 return session ? session->breakpointController() : nullptr; 0055 } 0056 0057 } // anonymous namespace 0058 0059 class KDevelop::BreakpointModelPrivate 0060 { 0061 public: 0062 bool dirty = false; 0063 bool dontUpdateMarks = false; 0064 QList<Breakpoint*> breakpoints; 0065 /// FIXME: this is just an ugly workaround to not leak deleted breakpoints 0066 /// a real fix would make sure that we actually delete breakpoints 0067 /// right when we delete them... aka remove Breakpoint::{set}deleted 0068 QList<Breakpoint*> deletedBreakpoints; 0069 }; 0070 0071 BreakpointModel::BreakpointModel(QObject* parent) 0072 : QAbstractTableModel(parent), 0073 d_ptr(new BreakpointModelPrivate) 0074 { 0075 connect(this, &BreakpointModel::dataChanged, this, &BreakpointModel::updateMarks); 0076 0077 if (KDevelop::ICore::self()->partController()) { //TODO remove if 0078 const auto parts = KDevelop::ICore::self()->partController()->parts(); 0079 for (KParts::Part* p : parts) { 0080 slotPartAdded(p); 0081 } 0082 connect(KDevelop::ICore::self()->partController(), 0083 &IPartController::partAdded, 0084 this, 0085 &BreakpointModel::slotPartAdded); 0086 } 0087 0088 0089 connect (KDevelop::ICore::self()->documentController(), 0090 &IDocumentController::textDocumentCreated, 0091 this, 0092 &BreakpointModel::textDocumentCreated); 0093 connect (KDevelop::ICore::self()->documentController(), 0094 &IDocumentController::documentSaved, 0095 this, &BreakpointModel::documentSaved); 0096 } 0097 0098 BreakpointModel::~BreakpointModel() 0099 { 0100 Q_D(BreakpointModel); 0101 0102 qDeleteAll(d->breakpoints); 0103 qDeleteAll(d->deletedBreakpoints); 0104 } 0105 0106 void BreakpointModel::slotPartAdded(KParts::Part* part) 0107 { 0108 if (auto doc = qobject_cast<KTextEditor::Document*>(part)) 0109 { 0110 auto *iface = qobject_cast<MarkInterface*>(doc); 0111 if( !iface ) 0112 return; 0113 0114 iface->setMarkDescription((MarkInterface::MarkTypes)BreakpointMark, i18n("Breakpoint")); 0115 iface->setMarkPixmap((MarkInterface::MarkTypes)BreakpointMark, *breakpointPixmap()); 0116 iface->setMarkPixmap((MarkInterface::MarkTypes)PendingBreakpointMark, *pendingBreakpointPixmap()); 0117 iface->setMarkPixmap((MarkInterface::MarkTypes)ReachedBreakpointMark, *reachedBreakpointPixmap()); 0118 iface->setMarkPixmap((MarkInterface::MarkTypes)DisabledBreakpointMark, *disabledBreakpointPixmap()); 0119 iface->setEditableMarks( MarkInterface::Bookmark | BreakpointMark ); 0120 updateMarks(); 0121 } 0122 } 0123 0124 void BreakpointModel::textDocumentCreated(KDevelop::IDocument* doc) 0125 { 0126 Q_D(const BreakpointModel); 0127 0128 KTextEditor::Document* const textDocument = doc->textDocument(); 0129 0130 if (qobject_cast<KTextEditor::MarkInterface*>(textDocument)) { 0131 // can't use new signal slot syntax here, MarkInterface is not a QObject 0132 connect(textDocument, SIGNAL(markChanged(KTextEditor::Document*,KTextEditor::Mark,KTextEditor::MarkInterface::MarkChangeAction)), 0133 this, SLOT(markChanged(KTextEditor::Document*,KTextEditor::Mark,KTextEditor::MarkInterface::MarkChangeAction))); 0134 connect(textDocument, SIGNAL(markContextMenuRequested(KTextEditor::Document*,KTextEditor::Mark,QPoint,bool&)), 0135 SLOT(markContextMenuRequested(KTextEditor::Document*,KTextEditor::Mark,QPoint,bool&))); 0136 } 0137 0138 // markChanged() is not triggered for loaded breakpoints, so get them a moving cursor now 0139 const QUrl docUrl = textDocument->url(); 0140 for (Breakpoint* breakpoint : qAsConst(d->breakpoints)) { 0141 if (docUrl == breakpoint->url()) { 0142 setupMovingCursor(textDocument, breakpoint); 0143 } 0144 } 0145 } 0146 0147 void BreakpointModel::markContextMenuRequested(Document* document, Mark mark, const QPoint &pos, bool& handled) 0148 { 0149 int type = mark.type; 0150 qCDebug(DEBUGGER) << type; 0151 0152 Breakpoint *b = nullptr; 0153 if ((type & AllBreakpointMarks)) { 0154 b = breakpoint(document->url(), mark.line); 0155 if (!b) { 0156 QMessageBox::critical(nullptr, i18n("Breakpoint not found"), i18n("Couldn't find breakpoint at %1:%2", document->url().toString(), mark.line)); 0157 } 0158 } else if (!(type & MarkInterface::Bookmark)) // neither breakpoint nor bookmark 0159 return; 0160 0161 QMenu menu; // TODO: needs qwidget 0162 QAction* breakpointAction = menu.addAction(QIcon::fromTheme(QStringLiteral("breakpoint")), i18n("&Breakpoint")); 0163 breakpointAction->setCheckable(true); 0164 breakpointAction->setChecked(b); 0165 QAction* enableAction = nullptr; 0166 if (b) { 0167 enableAction = b->enabled() ? 0168 menu.addAction(QIcon::fromTheme(QStringLiteral("dialog-cancel")), i18n("&Disable Breakpoint")) : 0169 menu.addAction(QIcon::fromTheme(QStringLiteral("dialog-ok-apply")), i18n("&Enable Breakpoint")); 0170 } 0171 menu.addSeparator(); 0172 QAction* bookmarkAction = menu.addAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), i18n("&Bookmark")); 0173 bookmarkAction->setCheckable(true); 0174 bookmarkAction->setChecked((type & MarkInterface::Bookmark)); 0175 0176 QAction* triggeredAction = menu.exec(pos); 0177 if (triggeredAction) { 0178 if (triggeredAction == bookmarkAction) { 0179 KTextEditor::MarkInterface *iface = qobject_cast<KTextEditor::MarkInterface*>(document); 0180 if ((type & MarkInterface::Bookmark)) 0181 iface->removeMark(mark.line, MarkInterface::Bookmark); 0182 else 0183 iface->addMark(mark.line, MarkInterface::Bookmark); 0184 } else if (triggeredAction == breakpointAction) { 0185 if (b) { 0186 removeBreakpoint(b); 0187 } else { 0188 Breakpoint* breakpoint = addCodeBreakpoint(document->url(), mark.line); 0189 setupMovingCursor(document, breakpoint); 0190 } 0191 } else if (triggeredAction == enableAction) { 0192 b->setData(Breakpoint::EnableColumn, b->enabled() ? Qt::Unchecked : Qt::Checked); 0193 } 0194 } 0195 0196 handled = true; 0197 } 0198 0199 0200 QVariant 0201 BreakpointModel::headerData(int section, Qt::Orientation orientation, 0202 int role) const 0203 { 0204 if (orientation == Qt::Vertical) 0205 return QVariant(); 0206 0207 if (role == Qt::DecorationRole ) { 0208 if (section == 0) 0209 return QIcon::fromTheme(QStringLiteral("dialog-ok-apply")); 0210 else if (section == 1) 0211 return QIcon::fromTheme(QStringLiteral("system-switch-user")); 0212 } 0213 0214 if (role == Qt::DisplayRole) { 0215 if (section == 0 || section == 1) return QString(); 0216 if (section == 2) return i18n("Type"); 0217 if (section == 3) return i18n("Location"); 0218 if (section == 4) return i18n("Condition"); 0219 } 0220 0221 if (role == Qt::ToolTipRole) { 0222 if (section == 0) return i18n("Active status"); 0223 if (section == 1) return i18n("State"); 0224 return headerData(section, orientation, Qt::DisplayRole); 0225 0226 } 0227 return QVariant(); 0228 } 0229 0230 Qt::ItemFlags BreakpointModel::flags(const QModelIndex &index) const 0231 { 0232 /* FIXME: all this logic must be in item */ 0233 if (!index.isValid()) 0234 return Qt::NoItemFlags; 0235 0236 if (index.column() == 0) 0237 return static_cast<Qt::ItemFlags>( 0238 Qt::ItemIsEnabled | Qt::ItemIsSelectable 0239 | Qt::ItemIsEditable | Qt::ItemIsUserCheckable); 0240 0241 if (index.column() == Breakpoint::ConditionColumn) 0242 return static_cast<Qt::ItemFlags>( 0243 Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); 0244 0245 return static_cast<Qt::ItemFlags>(Qt::ItemIsEnabled | Qt::ItemIsSelectable); 0246 } 0247 0248 QModelIndex BreakpointModel::breakpointIndex(KDevelop::Breakpoint* b, int column) 0249 { 0250 Q_D(BreakpointModel); 0251 0252 int row = d->breakpoints.indexOf(b); 0253 if (row == -1) return QModelIndex(); 0254 return index(row, column); 0255 } 0256 0257 bool KDevelop::BreakpointModel::removeRows(int row, int count, const QModelIndex& parent) 0258 { 0259 Q_D(BreakpointModel); 0260 0261 if (count < 1 || (row < 0) || (row + count) > rowCount(parent)) 0262 return false; 0263 0264 IBreakpointController* controller = breakpointController(); 0265 0266 beginRemoveRows(parent, row, row+count-1); 0267 for (int i=0; i < count; ++i) { 0268 Breakpoint* b = d->breakpoints.at(row); 0269 b->m_deleted = true; 0270 if (controller) 0271 controller->breakpointAboutToBeDeleted(row); 0272 d->breakpoints.removeAt(row); 0273 b->m_model = nullptr; 0274 // To be changed: the controller is currently still responsible for deleting the breakpoint 0275 // object 0276 // FIXME: this whole notion of m_deleted is utterly broken and needs to be fixed properly 0277 // for now just prevent a leak... 0278 d->deletedBreakpoints.append(b); 0279 } 0280 endRemoveRows(); 0281 updateMarks(); 0282 scheduleSave(); 0283 return true; 0284 } 0285 0286 int KDevelop::BreakpointModel::rowCount(const QModelIndex& parent) const 0287 { 0288 Q_D(const BreakpointModel); 0289 0290 if (!parent.isValid()) { 0291 return d->breakpoints.count(); 0292 } 0293 return 0; 0294 } 0295 0296 int KDevelop::BreakpointModel::columnCount(const QModelIndex& parent) const 0297 { 0298 Q_UNUSED(parent); 0299 return 5; 0300 } 0301 0302 QVariant BreakpointModel::data(const QModelIndex& index, int role) const 0303 { 0304 Q_D(const BreakpointModel); 0305 0306 if (!index.parent().isValid() && index.row() < d->breakpoints.count()) { 0307 return d->breakpoints.at(index.row())->data(index.column(), role); 0308 } 0309 return QVariant(); 0310 } 0311 0312 bool KDevelop::BreakpointModel::setData(const QModelIndex& index, const QVariant& value, int role) 0313 { 0314 Q_D(const BreakpointModel); 0315 0316 if (!index.parent().isValid() && index.row() < d->breakpoints.count() && (role == Qt::EditRole || role == Qt::CheckStateRole)) { 0317 return d->breakpoints.at(index.row())->setData(index.column(), value); 0318 } 0319 return false; 0320 } 0321 0322 void BreakpointModel::updateState(int row, Breakpoint::BreakpointState state) 0323 { 0324 Q_D(BreakpointModel); 0325 0326 Breakpoint* breakpoint = d->breakpoints.at(row); 0327 if (state != breakpoint->m_state) { 0328 breakpoint->m_state = state; 0329 reportChange(breakpoint, Breakpoint::StateColumn); 0330 } 0331 } 0332 0333 void BreakpointModel::updateHitCount(int row, int hitCount) 0334 { 0335 Q_D(BreakpointModel); 0336 0337 Breakpoint* breakpoint = d->breakpoints.at(row); 0338 if (hitCount != breakpoint->m_hitCount) { 0339 breakpoint->m_hitCount = hitCount; 0340 reportChange(breakpoint, Breakpoint::HitCountColumn); 0341 } 0342 } 0343 0344 void BreakpointModel::updateErrorText(int row, const QString& errorText) 0345 { 0346 Q_D(BreakpointModel); 0347 0348 Breakpoint* breakpoint = d->breakpoints.at(row); 0349 if (breakpoint->m_errorText != errorText) { 0350 breakpoint->m_errorText = errorText; 0351 reportChange(breakpoint, Breakpoint::StateColumn); 0352 } 0353 0354 if (!errorText.isEmpty()) { 0355 emit error(row, errorText); 0356 } 0357 } 0358 0359 void BreakpointModel::notifyHit(int row) 0360 { 0361 emit hit(row); 0362 } 0363 0364 void BreakpointModel::markChanged( 0365 KTextEditor::Document *document, 0366 KTextEditor::Mark mark, 0367 KTextEditor::MarkInterface::MarkChangeAction action) 0368 { 0369 int type = mark.type; 0370 /* Is this a breakpoint mark, to begin with? */ 0371 if (!(type & AllBreakpointMarks)) return; 0372 0373 if (action == KTextEditor::MarkInterface::MarkAdded) { 0374 Breakpoint *b = breakpoint(document->url(), mark.line); 0375 if (b) { 0376 //there was already a breakpoint, so delete instead of adding 0377 removeBreakpoint(b); 0378 return; 0379 } 0380 Breakpoint* breakpoint = addCodeBreakpoint(document->url(), mark.line); 0381 setupMovingCursor(document, breakpoint); 0382 } else { 0383 // Find this breakpoint and delete it 0384 Breakpoint *b = breakpoint(document->url(), mark.line); 0385 if (b) { 0386 removeBreakpoint(b); 0387 } 0388 } 0389 0390 #if 0 0391 if ( KDevelop::ICore::self()->documentController()->activeDocument() && KDevelop::ICore::self()->documentController()->activeDocument()->textDocument() == document ) 0392 { 0393 //bring focus back to the editor 0394 // TODO probably want a different command here 0395 KDevelop::ICore::self()->documentController()->activateDocument(KDevelop::ICore::self()->documentController()->activeDocument()); 0396 } 0397 #endif 0398 } 0399 0400 static constexpr int breakpointMarkPixmapSize = 32; 0401 0402 const QPixmap* BreakpointModel::breakpointPixmap() 0403 { 0404 static QPixmap pixmap=QIcon::fromTheme(QStringLiteral("breakpoint")).pixmap(QSize(breakpointMarkPixmapSize, breakpointMarkPixmapSize), QIcon::Active, QIcon::Off); 0405 return &pixmap; 0406 } 0407 0408 const QPixmap* BreakpointModel::pendingBreakpointPixmap() 0409 { 0410 static QPixmap pixmap=QIcon::fromTheme(QStringLiteral("breakpoint")).pixmap(QSize(breakpointMarkPixmapSize, breakpointMarkPixmapSize), QIcon::Normal, QIcon::Off); 0411 return &pixmap; 0412 } 0413 0414 const QPixmap* BreakpointModel::reachedBreakpointPixmap() 0415 { 0416 static QPixmap pixmap=QIcon::fromTheme(QStringLiteral("breakpoint")).pixmap(QSize(breakpointMarkPixmapSize, breakpointMarkPixmapSize), QIcon::Selected, QIcon::Off); 0417 return &pixmap; 0418 } 0419 0420 const QPixmap* BreakpointModel::disabledBreakpointPixmap() 0421 { 0422 static QPixmap pixmap=QIcon::fromTheme(QStringLiteral("breakpoint")).pixmap(QSize(breakpointMarkPixmapSize, breakpointMarkPixmapSize), QIcon::Disabled, QIcon::Off); 0423 return &pixmap; 0424 } 0425 0426 void BreakpointModel::removeBreakpoint(Breakpoint* breakpoint) 0427 { 0428 Q_D(BreakpointModel); 0429 0430 Q_ASSERT(breakpoint); 0431 const auto row = d->breakpoints.indexOf(breakpoint); 0432 Q_ASSERT(row != -1); 0433 removeRow(row); 0434 } 0435 0436 void BreakpointModel::toggleBreakpoint(const QUrl& url, const KTextEditor::Cursor& cursor) 0437 { 0438 Breakpoint *b = breakpoint(url, cursor.line()); 0439 if (b) { 0440 removeBreakpoint(b); 0441 } else { 0442 addCodeBreakpoint(url, cursor.line()); 0443 } 0444 } 0445 0446 void BreakpointModel::reportChange(Breakpoint* breakpoint, Breakpoint::Column column) 0447 { 0448 Q_D(BreakpointModel); 0449 0450 // note: just a portion of Breakpoint::Column is displayed in this model! 0451 if (column >= 0 && column < columnCount()) { 0452 QModelIndex idx = breakpointIndex(breakpoint, column); 0453 Q_ASSERT(idx.isValid()); // make sure we don't pass invalid indices to dataChanged() 0454 emit dataChanged(idx, idx); 0455 } 0456 0457 if (IBreakpointController* controller = breakpointController()) { 0458 int row = d->breakpoints.indexOf(breakpoint); 0459 Q_ASSERT(row != -1); 0460 controller->breakpointModelChanged(row, ColumnFlags(1 << column)); 0461 } 0462 0463 scheduleSave(); 0464 } 0465 0466 uint BreakpointModel::breakpointType(Breakpoint *breakpoint) const 0467 { 0468 uint type = BreakpointMark; 0469 if (!breakpoint->enabled()) { 0470 type = DisabledBreakpointMark; 0471 } else if (breakpoint->hitCount() > 0) { 0472 type = ReachedBreakpointMark; 0473 } else if (breakpoint->state() == Breakpoint::PendingState) { 0474 type = PendingBreakpointMark; 0475 } 0476 return type; 0477 } 0478 0479 void KDevelop::BreakpointModel::updateMarks() 0480 { 0481 Q_D(BreakpointModel); 0482 0483 if (d->dontUpdateMarks) 0484 return; 0485 0486 const auto* const documentController = ICore::self()->documentController(); 0487 if (!documentController) { 0488 qCDebug(DEBUGGER) << "Cannot update marks without the document controller. " 0489 "KDevelop must be exiting and the document controller already destroyed."; 0490 return; 0491 } 0492 0493 //add marks 0494 for (Breakpoint* breakpoint : qAsConst(d->breakpoints)) { 0495 if (breakpoint->kind() != Breakpoint::CodeBreakpoint) continue; 0496 if (breakpoint->line() == -1) continue; 0497 IDocument *doc = documentController->documentForUrl(breakpoint->url()); 0498 if (!doc) continue; 0499 KTextEditor::MarkInterface *mark = qobject_cast<KTextEditor::MarkInterface*>(doc->textDocument()); 0500 if (!mark) continue; 0501 uint type = breakpointType(breakpoint); 0502 IF_DEBUG( qCDebug(DEBUGGER) << type << breakpoint->url() << mark->mark(breakpoint->line()); ) 0503 0504 { 0505 QSignalBlocker blocker(doc->textDocument()); 0506 if (mark->mark(breakpoint->line()) & AllBreakpointMarks) { 0507 if (!(mark->mark(breakpoint->line()) & type)) { 0508 mark->removeMark(breakpoint->line(), AllBreakpointMarks); 0509 mark->addMark(breakpoint->line(), type); 0510 } 0511 } else { 0512 mark->addMark(breakpoint->line(), type); 0513 } 0514 } 0515 } 0516 0517 //remove marks 0518 const auto documents = documentController->openDocuments(); 0519 for (IDocument* doc : documents) { 0520 KTextEditor::MarkInterface *mark = qobject_cast<KTextEditor::MarkInterface*>(doc->textDocument()); 0521 if (!mark) continue; 0522 0523 { 0524 QSignalBlocker blocker(doc->textDocument()); 0525 const auto oldMarks = mark->marks(); 0526 for (KTextEditor::Mark* m : oldMarks) { 0527 if (!(m->type & AllBreakpointMarks)) continue; 0528 IF_DEBUG( qCDebug(DEBUGGER) << m->line << m->type; ) 0529 for (Breakpoint* breakpoint : qAsConst(d->breakpoints)) { 0530 if (breakpoint->kind() != Breakpoint::CodeBreakpoint) continue; 0531 if (doc->url() == breakpoint->url() && m->line == breakpoint->line()) { 0532 goto continueNextMark; 0533 } 0534 } 0535 mark->removeMark(m->line, AllBreakpointMarks); 0536 continueNextMark:; 0537 } 0538 } 0539 } 0540 } 0541 0542 void BreakpointModel::documentSaved(KDevelop::IDocument* doc) 0543 { 0544 Q_D(BreakpointModel); 0545 0546 IF_DEBUG( qCDebug(DEBUGGER); ) 0547 for (Breakpoint* breakpoint : qAsConst(d->breakpoints)) { 0548 if (breakpoint->movingCursor()) { 0549 if (breakpoint->movingCursor()->document() != doc->textDocument()) continue; 0550 if (breakpoint->movingCursor()->line() == breakpoint->line()) continue; 0551 d->dontUpdateMarks = true; 0552 breakpoint->setLine(breakpoint->movingCursor()->line()); 0553 d->dontUpdateMarks = false; 0554 } 0555 } 0556 } 0557 void BreakpointModel::aboutToDeleteMovingInterfaceContent(KTextEditor::Document* document) 0558 { 0559 Q_D(BreakpointModel); 0560 0561 for (Breakpoint* breakpoint : qAsConst(d->breakpoints)) { 0562 if (breakpoint->movingCursor() && breakpoint->movingCursor()->document() == document) { 0563 breakpoint->setMovingCursor(nullptr); 0564 } 0565 } 0566 } 0567 0568 void BreakpointModel::load() 0569 { 0570 KConfigGroup breakpoints = ICore::self()->activeSession()->config()->group("Breakpoints"); 0571 int count = breakpoints.readEntry("number", 0); 0572 if (count == 0) 0573 return; 0574 0575 beginInsertRows(QModelIndex(), 0, count - 1); 0576 for (int i = 0; i < count; ++i) { 0577 if (!breakpoints.group(QString::number(i)).readEntry("kind", "").isEmpty()) { 0578 new Breakpoint(this, breakpoints.group(QString::number(i))); 0579 } 0580 } 0581 endInsertRows(); 0582 } 0583 0584 void BreakpointModel::save() 0585 { 0586 Q_D(BreakpointModel); 0587 0588 d->dirty = false; 0589 0590 auto* const activeSession = ICore::self()->activeSession(); 0591 if (!activeSession) { 0592 qCDebug(DEBUGGER) << "Cannot save breakpoints because there is no active session. " 0593 "KDevelop must be exiting and already past SessionController::cleanup()."; 0594 return; 0595 } 0596 0597 KConfigGroup breakpoints = activeSession->config()->group("Breakpoints"); 0598 breakpoints.writeEntry("number", d->breakpoints.count()); 0599 int i = 0; 0600 for (Breakpoint* b : qAsConst(d->breakpoints)) { 0601 KConfigGroup g = breakpoints.group(QString::number(i)); 0602 b->save(g); 0603 ++i; 0604 } 0605 breakpoints.sync(); 0606 } 0607 0608 void BreakpointModel::scheduleSave() 0609 { 0610 Q_D(BreakpointModel); 0611 0612 if (d->dirty) 0613 return; 0614 0615 d->dirty = true; 0616 QTimer::singleShot(0, this, &BreakpointModel::save); 0617 } 0618 0619 QList<Breakpoint*> KDevelop::BreakpointModel::breakpoints() const 0620 { 0621 Q_D(const BreakpointModel); 0622 0623 return d->breakpoints; 0624 } 0625 0626 Breakpoint* BreakpointModel::breakpoint(int row) const 0627 { 0628 Q_D(const BreakpointModel); 0629 0630 if (row >= d->breakpoints.count()) return nullptr; 0631 return d->breakpoints.at(row); 0632 } 0633 0634 Breakpoint* BreakpointModel::addCodeBreakpoint() 0635 { 0636 Q_D(BreakpointModel); 0637 0638 beginInsertRows(QModelIndex(), d->breakpoints.count(), d->breakpoints.count()); 0639 auto* n = new Breakpoint(this, Breakpoint::CodeBreakpoint); 0640 endInsertRows(); 0641 return n; 0642 } 0643 0644 Breakpoint* BreakpointModel::addCodeBreakpoint(const QUrl& url, int line) 0645 { 0646 Breakpoint* n = addCodeBreakpoint(); 0647 n->setLocation(url, line); 0648 return n; 0649 } 0650 0651 Breakpoint* BreakpointModel::addCodeBreakpoint(const QString& expression) 0652 { 0653 Breakpoint* n = addCodeBreakpoint(); 0654 n->setExpression(expression); 0655 return n; 0656 } 0657 0658 Breakpoint* BreakpointModel::addWatchpoint() 0659 { 0660 Q_D(BreakpointModel); 0661 0662 beginInsertRows(QModelIndex(), d->breakpoints.count(), d->breakpoints.count()); 0663 auto* n = new Breakpoint(this, Breakpoint::WriteBreakpoint); 0664 endInsertRows(); 0665 return n; 0666 } 0667 0668 Breakpoint* BreakpointModel::addWatchpoint(const QString& expression) 0669 { 0670 Breakpoint* n = addWatchpoint(); 0671 n->setExpression(expression); 0672 return n; 0673 } 0674 0675 Breakpoint* BreakpointModel::addReadWatchpoint() 0676 { 0677 Q_D(BreakpointModel); 0678 0679 beginInsertRows(QModelIndex(), d->breakpoints.count(), d->breakpoints.count()); 0680 auto* n = new Breakpoint(this, Breakpoint::ReadBreakpoint); 0681 endInsertRows(); 0682 return n; 0683 } 0684 0685 Breakpoint* BreakpointModel::addReadWatchpoint(const QString& expression) 0686 { 0687 Breakpoint* n = addReadWatchpoint(); 0688 n->setExpression(expression); 0689 return n; 0690 } 0691 0692 Breakpoint* BreakpointModel::addAccessWatchpoint() 0693 { 0694 Q_D(BreakpointModel); 0695 0696 beginInsertRows(QModelIndex(), d->breakpoints.count(), d->breakpoints.count()); 0697 auto* n = new Breakpoint(this, Breakpoint::AccessBreakpoint); 0698 endInsertRows(); 0699 return n; 0700 } 0701 0702 0703 Breakpoint* BreakpointModel::addAccessWatchpoint(const QString& expression) 0704 { 0705 Breakpoint* n = addAccessWatchpoint(); 0706 n->setExpression(expression); 0707 return n; 0708 } 0709 0710 void BreakpointModel::registerBreakpoint(Breakpoint* breakpoint) 0711 { 0712 Q_D(BreakpointModel); 0713 0714 Q_ASSERT(!d->breakpoints.contains(breakpoint)); 0715 int row = d->breakpoints.size(); 0716 d->breakpoints << breakpoint; 0717 if (IBreakpointController* controller = breakpointController()) { 0718 controller->breakpointAdded(row); 0719 } 0720 scheduleSave(); 0721 } 0722 0723 Breakpoint* BreakpointModel::breakpoint(const QUrl& url, int line) const 0724 { 0725 Q_D(const BreakpointModel); 0726 0727 auto it = std::find_if(d->breakpoints.constBegin(), d->breakpoints.constEnd(), [&](Breakpoint* b) { 0728 return (b->url() == url && b->line() == line); 0729 }); 0730 return (it != d->breakpoints.constEnd()) ? *it : nullptr; 0731 } 0732 0733 void BreakpointModel::setupMovingCursor(KTextEditor::Document* document, Breakpoint* breakpoint) const 0734 { 0735 Q_ASSERT(document->url() == breakpoint->url() && breakpoint->movingCursor() == nullptr); 0736 0737 auto* const movingInterface = qobject_cast<KTextEditor::MovingInterface*>(document); 0738 if (movingInterface) { 0739 auto* const cursor = movingInterface->newMovingCursor(KTextEditor::Cursor(breakpoint->line(), 0)); 0740 // can't use new signal/slot syntax here, MovingInterface is not a QObject 0741 connect(document, SIGNAL(aboutToDeleteMovingInterfaceContent(KTextEditor::Document*)), 0742 this, SLOT(aboutToDeleteMovingInterfaceContent(KTextEditor::Document*)), Qt::UniqueConnection); 0743 breakpoint->setMovingCursor(cursor); 0744 } 0745 } 0746 0747 #include "moc_breakpointmodel.cpp"