mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-26 20:03:10 +00:00
250 lines
9.1 KiB
C++
250 lines
9.1 KiB
C++
![]() |
/*
|
||
|
* This file is part of KDevelop
|
||
|
*
|
||
|
* Copyright 1999 John Birch <jbb@kdevelop.org>
|
||
|
* Copyright 2007 Hamish Rodda <rodda@kde.org>
|
||
|
* Copyright 2009 Niko Sams <niko.sams@gmail.com>
|
||
|
* Copyright 2009 Aleix Pol <aleixpol@kde.org>
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as
|
||
|
* published by the Free Software Foundation; either version 2 of the
|
||
|
* License, or (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public
|
||
|
* License along with this program; if not, write to the
|
||
|
* Free Software Foundation, Inc.,
|
||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
*/
|
||
|
|
||
|
#include "framestackwidget.h"
|
||
|
|
||
|
#include <QHeaderView>
|
||
|
#include <QScrollBar>
|
||
|
#include <QListView>
|
||
|
#include <QVBoxLayout>
|
||
|
#include <QLabel>
|
||
|
#include <QTreeView>
|
||
|
#include <QMenu>
|
||
|
#include <QApplication>
|
||
|
#include <QClipboard>
|
||
|
#include <QResizeEvent>
|
||
|
|
||
|
#include <KAction>
|
||
|
#include <KStandardAction>
|
||
|
#include <KDebug>
|
||
|
#include <KLocalizedString>
|
||
|
#include <KIcon>
|
||
|
#include <KTextEditor/Cursor>
|
||
|
|
||
|
#include <interfaces/icore.h>
|
||
|
#include <interfaces/idebugcontroller.h>
|
||
|
#include <interfaces/idocumentcontroller.h>
|
||
|
#include "framestackmodel.h"
|
||
|
|
||
|
namespace KDevelop {
|
||
|
|
||
|
FramestackWidget::FramestackWidget(IDebugController* controller, QWidget* parent)
|
||
|
: AutoOrientedSplitter(Qt::Horizontal, parent), m_session(0)
|
||
|
{
|
||
|
connect(controller,
|
||
|
SIGNAL(currentSessionChanged(KDevelop::IDebugSession*)),
|
||
|
SLOT(currentSessionChanged(KDevelop::IDebugSession*)));
|
||
|
connect(controller, SIGNAL(raiseFramestackViews()), SIGNAL(requestRaise()));
|
||
|
|
||
|
setWhatsThis(i18n("<b>Frame stack</b>"
|
||
|
"Often referred to as the \"call stack\", "
|
||
|
"this is a list showing which function is "
|
||
|
"currently active, and what called each "
|
||
|
"function to get to this point in your "
|
||
|
"program. By clicking on an item you "
|
||
|
"can see the values in any of the "
|
||
|
"previous calling functions."));
|
||
|
setWindowIcon(KIcon("view-list-text"));
|
||
|
m_threadsWidget = new QWidget(this);
|
||
|
m_threads = new QListView(m_threadsWidget);
|
||
|
m_frames = new QTreeView(this);
|
||
|
m_frames->setRootIsDecorated(false);
|
||
|
m_frames->setSelectionMode(QAbstractItemView::ContiguousSelection);
|
||
|
m_frames->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||
|
m_frames->setAllColumnsShowFocus(true);
|
||
|
m_frames->setContextMenuPolicy(Qt::CustomContextMenu);
|
||
|
|
||
|
m_framesContextMenu = new QMenu(m_frames);
|
||
|
|
||
|
KAction *selectAllAction = KStandardAction::selectAll(m_frames);
|
||
|
selectAllAction->setShortcut(KShortcut()); //FIXME: why does CTRL-A conflict with Katepart (while CTRL-Cbelow doesn't) ?
|
||
|
selectAllAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
||
|
connect(selectAllAction, SIGNAL(triggered()), SLOT(selectAll()));
|
||
|
m_framesContextMenu->addAction(selectAllAction);
|
||
|
|
||
|
KAction *copyAction = KStandardAction::copy(m_frames);
|
||
|
copyAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
||
|
connect(copyAction, SIGNAL(triggered()), SLOT(copySelection()));
|
||
|
m_framesContextMenu->addAction(copyAction);
|
||
|
addAction(copyAction);
|
||
|
|
||
|
connect(m_frames, SIGNAL(customContextMenuRequested(QPoint)), SLOT(frameContextMenuRequested(QPoint)));
|
||
|
|
||
|
m_threadsWidget->setLayout(new QVBoxLayout());
|
||
|
m_threadsWidget->layout()->addWidget(new QLabel(i18n("Threads:")));
|
||
|
m_threadsWidget->layout()->addWidget(m_threads);
|
||
|
m_threadsWidget->hide();
|
||
|
addWidget(m_threadsWidget);
|
||
|
addWidget(m_frames);
|
||
|
|
||
|
setStretchFactor(1, 3);
|
||
|
connect(m_frames->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(checkFetchMoreFrames()));
|
||
|
|
||
|
// Show the selected frame when clicked, even if it has previously been selected
|
||
|
connect(m_frames, SIGNAL(clicked(QModelIndex)), SLOT(frameSelectionChanged(QModelIndex)));
|
||
|
}
|
||
|
|
||
|
FramestackWidget::~FramestackWidget() {}
|
||
|
|
||
|
void FramestackWidget::currentSessionChanged(KDevelop::IDebugSession* session)
|
||
|
{
|
||
|
kDebug() << "Adding session:" << isVisible();
|
||
|
|
||
|
m_session = session;
|
||
|
|
||
|
m_threads->setModel(session ? session->frameStackModel() : 0);
|
||
|
m_frames->setModel(session ? session->frameStackModel() : 0);
|
||
|
|
||
|
if (session) {
|
||
|
connect(session->frameStackModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
|
||
|
this, SLOT(checkFetchMoreFrames()));
|
||
|
connect(session->frameStackModel(), SIGNAL(currentThreadChanged(int)),
|
||
|
SLOT(currentThreadChanged(int)));
|
||
|
currentThreadChanged(session->frameStackModel()->currentThread());
|
||
|
connect(session->frameStackModel(), SIGNAL(currentFrameChanged(int)),
|
||
|
SLOT(currentFrameChanged(int)));
|
||
|
currentFrameChanged(session->frameStackModel()->currentFrame());
|
||
|
connect(session, SIGNAL(stateChanged(KDevelop::IDebugSession::DebuggerState)),
|
||
|
SLOT(sessionStateChanged(KDevelop::IDebugSession::DebuggerState)));
|
||
|
|
||
|
connect(m_threads->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
|
||
|
this, SLOT(setThreadShown(QModelIndex)));
|
||
|
|
||
|
// Show the selected frame, independent of the means by which it has been selected
|
||
|
connect(m_frames->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
|
||
|
this, SLOT(frameSelectionChanged(QModelIndex)));
|
||
|
}
|
||
|
|
||
|
if (isVisible()) {
|
||
|
showEvent(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FramestackWidget::hideEvent(QHideEvent* e)
|
||
|
{
|
||
|
QWidget::hideEvent(e);
|
||
|
}
|
||
|
|
||
|
void FramestackWidget::showEvent(QShowEvent* e)
|
||
|
{
|
||
|
QWidget::showEvent(e);
|
||
|
}
|
||
|
|
||
|
void FramestackWidget::setThreadShown(const QModelIndex& current)
|
||
|
{
|
||
|
if (!current.isValid())
|
||
|
return;
|
||
|
m_session->frameStackModel()->setCurrentThread(current);
|
||
|
}
|
||
|
|
||
|
void FramestackWidget::checkFetchMoreFrames()
|
||
|
{
|
||
|
int val = m_frames->verticalScrollBar()->value();
|
||
|
int max = m_frames->verticalScrollBar()->maximum();
|
||
|
const int offset = 20;
|
||
|
|
||
|
if (val + offset > max && m_session) {
|
||
|
m_session->frameStackModel()->fetchMoreFrames();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FramestackWidget::currentThreadChanged(int thread)
|
||
|
{
|
||
|
if (thread != -1) {
|
||
|
IFrameStackModel* model = m_session->frameStackModel();
|
||
|
QModelIndex idx = model->currentThreadIndex();
|
||
|
m_threads->selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
|
||
|
m_threadsWidget->setVisible(model->rowCount() > 1);
|
||
|
m_frames->setRootIndex(idx);
|
||
|
m_frames->header()->setResizeMode(0, QHeaderView::ResizeToContents);
|
||
|
} else {
|
||
|
m_threadsWidget->hide();
|
||
|
m_threads->selectionModel()->clear();
|
||
|
m_frames->setRootIndex(QModelIndex());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FramestackWidget::currentFrameChanged(int frame)
|
||
|
{
|
||
|
if (frame != -1) {
|
||
|
IFrameStackModel* model = m_session->frameStackModel();
|
||
|
QModelIndex idx = model->currentFrameIndex();
|
||
|
m_frames->selectionModel()->select(
|
||
|
idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
|
||
|
} else {
|
||
|
m_frames->selectionModel()->clear();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FramestackWidget::frameSelectionChanged(const QModelIndex& current /* previous */)
|
||
|
{
|
||
|
if (!current.isValid())
|
||
|
return;
|
||
|
IFrameStackModel::FrameItem f = m_session->frameStackModel()->frame(current);
|
||
|
/* If line is -1, then it's not a source file at all. */
|
||
|
if (f.line != -1) {
|
||
|
QPair<KUrl, int> file = m_session->convertToLocalUrl(qMakePair(f.file, f.line));
|
||
|
ICore::self()->documentController()->openDocument(file.first, KTextEditor::Cursor(file.second, 0), IDocumentController::DoNotFocus);
|
||
|
}
|
||
|
|
||
|
m_session->frameStackModel()->setCurrentFrame(f.nr);
|
||
|
}
|
||
|
|
||
|
void FramestackWidget::frameContextMenuRequested(const QPoint& pos)
|
||
|
{
|
||
|
m_framesContextMenu->popup( m_frames->mapToGlobal(pos) + QPoint(0, m_frames->header()->height()) );
|
||
|
}
|
||
|
|
||
|
void FramestackWidget::copySelection()
|
||
|
{
|
||
|
QClipboard *cb = QApplication::clipboard();
|
||
|
QModelIndexList indexes = m_frames->selectionModel()->selectedRows();
|
||
|
QString content;
|
||
|
Q_FOREACH( QModelIndex index, indexes) {
|
||
|
IFrameStackModel::FrameItem frame = m_session->frameStackModel()->frame(index);
|
||
|
if (frame.line == -1) {
|
||
|
content += QString("#%1 %2() at %3\n").arg(frame.nr).arg(frame.name).arg(frame.file.pathOrUrl(KUrl::RemoveTrailingSlash));
|
||
|
} else {
|
||
|
content += QString("#%1 %2() at %3:%4\n").arg(frame.nr).arg(frame.name).arg(frame.file.pathOrUrl(KUrl::RemoveTrailingSlash)).arg(frame.line+1);
|
||
|
}
|
||
|
}
|
||
|
cb->setText(content);
|
||
|
}
|
||
|
|
||
|
void FramestackWidget::selectAll()
|
||
|
{
|
||
|
m_frames->selectAll();
|
||
|
}
|
||
|
|
||
|
void FramestackWidget::sessionStateChanged(KDevelop::IDebugSession::DebuggerState state)
|
||
|
{
|
||
|
bool enable = state == IDebugSession::PausedState || state == IDebugSession::StoppedState;
|
||
|
m_frames->setEnabled(enable);
|
||
|
m_threads->setEnabled(enable);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
#include "moc_framestackwidget.cpp"
|