diff --git a/debugger/framestack/framestackmodel.cpp b/debugger/framestack/framestackmodel.cpp index 950a2a64f8..960a93b80a 100644 --- a/debugger/framestack/framestackmodel.cpp +++ b/debugger/framestack/framestackmodel.cpp @@ -1,353 +1,353 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2009 Niko Sams * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library 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 Library 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 "framestackmodel.h" #include #include #include #include #include "../../interfaces/icore.h" #include "../../interfaces/idebugcontroller.h" #include "../../interfaces/iprojectcontroller.h" #include "../interfaces/isession.h" #include "util/debug.h" namespace KDevelop { FrameStackModel::FrameStackModel(IDebugSession *session) : IFrameStackModel(session) , m_currentThread(-1) , m_currentFrame(-1) , m_subsequentFrameFetchOperations(0) , m_updateCurrentFrameOnNextFetch(false) { connect(session, &IDebugSession::stateChanged, this, &FrameStackModel::stateChanged); } FrameStackModel::~FrameStackModel() { } void FrameStackModel::setThreads(const QList &threads) { qCDebug(DEBUGGER) << threads.count(); if (!m_threads.isEmpty()) { beginRemoveRows(QModelIndex(), 0, m_threads.count()-1); m_threads.clear(); endRemoveRows(); } if (!threads.isEmpty()) { beginInsertRows(QModelIndex(), 0, threads.count()-1); m_threads = threads; endInsertRows(); } } QModelIndex FrameStackModel::indexForThreadNumber(int threadNumber) { int i=0; foreach (const ThreadItem &t, m_threads) { if (t.nr == threadNumber) { return index(i, 0); } i++; } return QModelIndex(); } void FrameStackModel::setFrames(int threadNumber, QList frames) { QModelIndex threadIndex = indexForThreadNumber(threadNumber); Q_ASSERT(threadIndex.isValid()); if (!m_frames[threadNumber].isEmpty()) { beginRemoveRows(threadIndex, 0, m_frames[threadNumber].count()-1); m_frames[threadNumber].clear(); endRemoveRows(); } if (!frames.isEmpty()) { beginInsertRows(threadIndex, 0, frames.count()-1); m_frames[threadNumber] = frames; endInsertRows(); } if (m_currentThread == threadNumber && m_updateCurrentFrameOnNextFetch) { m_currentFrame = 0; m_updateCurrentFrameOnNextFetch = false; } session()->raiseEvent(IDebugSession::thread_or_frame_changed); // FIXME: Ugly hack. Apparently, when rows are added, the selection // in the view is cleared. Emit this so that some frame is still // selected. emit currentFrameChanged(m_currentFrame); } void FrameStackModel::insertFrames(int threadNumber, const QList &frames) { QModelIndex threadIndex = indexForThreadNumber(threadNumber); Q_ASSERT(threadIndex.isValid()); beginInsertRows(threadIndex, m_frames[threadNumber].count()-1, m_frames[threadNumber].count()+frames.count()-1); m_frames[threadNumber] << frames; endInsertRows(); } void FrameStackModel::setHasMoreFrames(int threadNumber, bool hasMoreFrames) { m_hasMoreFrames[threadNumber] = hasMoreFrames; } FrameStackModel::FrameItem FrameStackModel::frame(const QModelIndex& index) { Q_ASSERT(index.internalId()); - Q_ASSERT(m_threads.count() >= index.internalId()); + Q_ASSERT(static_cast(m_threads.count()) >= index.internalId()); const ThreadItem &thread = m_threads.at(index.internalId()-1); return m_frames[thread.nr].at(index.row()); } QList FrameStackModel::frames(int threadNumber) const { return m_frames.value(threadNumber); } QVariant FrameStackModel::data(const QModelIndex& index, int role) const { if (!index.internalId()) { //thread if (m_threads.count() <= index.row()) return QVariant(); const ThreadItem &thread = m_threads.at(index.row()); if (index.column() == 0) { if (role == Qt::DisplayRole) { return i18nc("#thread-id at function-name or address", "#%1 at %2", thread.nr, thread.name); } } } else { //frame - if (m_threads.count() < index.internalId()) return QVariant(); + if (static_cast(m_threads.count()) < index.internalId()) return QVariant(); const ThreadItem &thread = m_threads.at(index.internalId()-1); if (m_frames[thread.nr].count() <= index.row()) return QVariant(); const FrameItem &frame = m_frames[thread.nr].at(index.row()); if (index.column() == 0) { if (role == Qt::DisplayRole) { return QVariant(QString::number(frame.nr)); } } else if (index.column() == 1) { if (role == Qt::DisplayRole) { return QVariant(frame.name); } } else if (index.column() == 2) { if (role == Qt::DisplayRole) { QString ret = ICore::self()->projectController() ->prettyFileName(frame.file, IProjectController::FormatPlain); if (frame.line != -1) { ret += ':' + QString::number(frame.line + 1); } return ret; } else if (role == Qt::DecorationRole) { QMimeType mime = QMimeDatabase().mimeTypeForUrl(frame.file); return QIcon::fromTheme(mime.iconName()); } } } return QVariant(); } int FrameStackModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent); return 3; } int FrameStackModel::rowCount(const QModelIndex& parent) const { if (!parent.isValid()) { return m_threads.count(); } else if (!parent.internalId() && parent.column() == 0) { if (parent.row() < m_threads.count()) { return m_frames[m_threads.at(parent.row()).nr].count(); } } return 0; } QModelIndex FrameStackModel::parent(const QModelIndex& child) const { if (!child.internalId()) { return QModelIndex(); } else { return index(child.internalId()-1, 0); } } QModelIndex FrameStackModel::index(int row, int column, const QModelIndex& parent) const { if (parent.isValid()) { Q_ASSERT(!parent.internalId()); Q_ASSERT(parent.column() == 0); Q_ASSERT(parent.row() < m_threads.count()); return createIndex(row, column, parent.row()+1); } else { return createIndex(row, column); } } QVariant FrameStackModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (section == 0) { return i18n("Depth"); } else if (section == 1) { return i18n("Function"); } else if (section == 2) { return i18n("Source"); } } return QAbstractItemModel::headerData(section, orientation, role); } void FrameStackModel::setCurrentThread(int threadNumber) { qCDebug(DEBUGGER) << threadNumber; if (m_currentThread != threadNumber && threadNumber != -1) { // FIXME: this logic means that if we switch to thread 3 and // then to thread 2 and then to thread 3, we'll request frames // for thread 3 again, even if the program was not run in between // and therefore frames could not have changed. m_currentFrame = 0; //set before fetchFrames else --frame argument would be wrong m_updateCurrentFrameOnNextFetch = true; fetchFrames(threadNumber, 0, 20); } if (threadNumber != m_currentThread) { m_currentFrame = 0; qCDebug(DEBUGGER) << "currentFrame" << m_currentFrame; m_currentThread = threadNumber; emit currentFrameChanged(m_currentFrame); } qCDebug(DEBUGGER) << "currentThread: " << m_currentThread << "currentFrame: " << m_currentFrame; emit currentThreadChanged(threadNumber); session()->raiseEvent(IDebugSession::thread_or_frame_changed); } void FrameStackModel::setCurrentThread(const QModelIndex& index) { Q_ASSERT(index.isValid()); Q_ASSERT(!index.internalId()); Q_ASSERT(index.column() == 0); setCurrentThread(m_threads[index.row()].nr); } int FrameStackModel::currentThread() const { return m_currentThread; } QModelIndex FrameStackModel::currentThreadIndex() const { int i = 0; foreach (const ThreadItem &t, m_threads) { if (t.nr == currentThread()) { return index(i, 0); } ++i; } return QModelIndex(); } int FrameStackModel::currentFrame() const { return m_currentFrame; } QModelIndex FrameStackModel::currentFrameIndex() const { QModelIndex idx = currentThreadIndex(); return idx.child(m_currentFrame, 0); } void FrameStackModel::setCurrentFrame(int frame) { qCDebug(DEBUGGER) << frame; if (frame != m_currentFrame) { m_currentFrame = frame; session()->raiseEvent(IDebugSession::thread_or_frame_changed); emit currentFrameChanged(frame); } } void FrameStackModel::update() { m_subsequentFrameFetchOperations = 0; fetchThreads(); if (m_currentThread != -1) { fetchFrames(m_currentThread, 0, 20); } } void FrameStackModel::handleEvent(IDebugSession::event_t event) { switch (event) { case IDebugSession::program_state_changed: update(); break; default: break; } } void FrameStackModel::stateChanged(IDebugSession::DebuggerState state) { if (state == IDebugSession::PausedState) { setCurrentFrame(-1); m_updateCurrentFrameOnNextFetch = true; } else if (state == IDebugSession::EndedState || state == IDebugSession::NotStartedState) { setThreads(QList()); } } // FIXME: it should be possible to fetch more frames for // an arbitrary thread, without making it current. void FrameStackModel::fetchMoreFrames() { m_subsequentFrameFetchOperations += 1; const int fetch = 20 * m_subsequentFrameFetchOperations * m_subsequentFrameFetchOperations; if (m_currentThread != -1 && m_hasMoreFrames[m_currentThread]) { setHasMoreFrames(m_currentThread, false); fetchFrames(m_currentThread, m_frames[m_currentThread].count(), m_frames[m_currentThread].count()-1+fetch); } } } diff --git a/debugger/variable/variablewidget.cpp b/debugger/variable/variablewidget.cpp index b8eab5952a..e4daec230e 100644 --- a/debugger/variable/variablewidget.cpp +++ b/debugger/variable/variablewidget.cpp @@ -1,500 +1,500 @@ // ************************************************************************** // begin : Sun Aug 8 1999 // copyright : (C) 1999 by John Birch // email : jbb@kdevelop.org // ************************************************************************** // * Copyright 2006 Vladimir Prus // ************************************************************************** // * * // * 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. * // * * // ************************************************************************** #include "variablewidget.h" #include #include #include #include #include #include #include #include #include "../../interfaces/icore.h" #include #include "../interfaces/ivariablecontroller.h" #include "variablecollection.h" #include "util/debug.h" /** The variables widget is passive, and is invoked by the rest of the code via two main Q_SLOTS: - slotDbgStatus - slotCurrentFrame The first is received the program status changes and the second is received after current frame in the debugger can possibly changes. The widget has a list item for each frame/thread combination, with variables as children. However, at each moment only one item is shown. When handling the slotCurrentFrame, we check if variables for the current frame are available. If yes, we simply show the corresponding item. Otherwise, we fetch the new data from debugger. Fetching the data is done by emitting the produceVariablesInfo signal. In response, we get slotParametersReady and slotLocalsReady signal, in that order. The data is parsed and changed variables are highlighted. After that, we 'trim' variable items that were not reported by gdb -- that is, gone out of scope. */ // ************************************************************************** // ************************************************************************** // ************************************************************************** namespace KDevelop { VariableCollection *variableCollection() { return ICore::self()->debugController()->variableCollection(); } VariableWidget::VariableWidget(IDebugController* controller, QWidget *parent) : QWidget(parent), variablesRoot_(controller->variableCollection()->root()) { //setWindowIcon(QIcon::fromTheme("math_brace")); setWindowIcon(QIcon::fromTheme("debugger")); setWindowTitle(i18n("Debugger Variables")); varTree_ = new VariableTree(controller, this); setFocusProxy(varTree_); watchVarEditor_ = new KHistoryComboBox( this ); QVBoxLayout *topLayout = new QVBoxLayout(this); topLayout->addWidget(varTree_, 10); topLayout->addWidget(watchVarEditor_); topLayout->setMargin(0); connect(watchVarEditor_, static_cast(&KHistoryComboBox::returnPressed), this, &VariableWidget::slotAddWatch); //TODO //connect(plugin, SIGNAL(raiseVariableViews()), this, SIGNAL(requestRaise())); // Setup help items. setWhatsThis( i18n( "Variable tree" "The variable tree allows you to see the values of local " "variables and arbitrary expressions.
" "Local variables are displayed automatically and are updated " "as you step through your program. " "For each expression you enter, you can either evaluate it once, " "or \"watch\" it (make it auto-updated). Expressions that are not " "auto-updated can be updated manually from the context menu. " "Expressions can be renamed to more descriptive names by clicking " "on the name column.
" "To change the value of a variable or an expression, " "click on the value.
")); watchVarEditor_->setWhatsThis( i18n("Expression entry" "Type in expression to watch.")); } void VariableWidget::slotAddWatch(const QString &expression) { if (!expression.isEmpty()) { watchVarEditor_->addToHistory(expression); qCDebug(DEBUGGER) << "Trying to add watch\n"; Variable* v = variablesRoot_->watches()->add(expression); if (v) { - QModelIndex index = variableCollection()->indexForItem(v, 0); /* For watches on structures, we really do want them to be shown expanded. Except maybe for structure with custom pretty printing, but will handle that later. FIXME: it does not actually works now. */ + //QModelIndex index = variableCollection()->indexForItem(v, 0); //varTree_->setExpanded(index, true); } watchVarEditor_->clearEditText(); } } void VariableWidget::hideEvent(QHideEvent* e) { QWidget::hideEvent(e); variableCollection()->variableWidgetHidden(); } void VariableWidget::showEvent(QShowEvent* e) { QWidget::showEvent(e); variableCollection()->variableWidgetShown(); } // ************************************************************************** // ************************************************************************** // ************************************************************************** VariableTree::VariableTree(IDebugController *controller, VariableWidget *parent) : AsyncTreeView(controller->variableCollection(), parent) #if 0 , activePopup_(0), toggleWatch_(0) #endif { setRootIsDecorated(true); setAllColumnsShowFocus(true); QModelIndex index = controller->variableCollection()->indexForItem( controller->variableCollection()->watches(), 0); setExpanded(index, true); m_signalMapper = new QSignalMapper(this); setupActions(); } VariableCollection* VariableTree::collection() const { Q_ASSERT(qobject_cast(model())); return static_cast(model()); } VariableTree::~VariableTree() { } void VariableTree::setupActions() { // TODO decorate this properly to make nice menu title m_contextMenuTitle = new QAction(this); m_contextMenuTitle->setEnabled(false); // make Format menu action group m_formatMenu = new QMenu(i18n("&Format"), this); QActionGroup *ag= new QActionGroup(m_formatMenu); QAction* act; act = new QAction(i18n("&Natural"), ag); act->setData(Variable::Natural); act->setShortcut(Qt::Key_N); m_formatMenu->addAction(act); act = new QAction(i18n("&Binary"), ag); act->setData(Variable::Binary); act->setShortcut(Qt::Key_B); m_formatMenu->addAction(act); act = new QAction(i18n("&Octal"), ag); act->setData(Variable::Octal); act->setShortcut(Qt::Key_O); m_formatMenu->addAction(act); act = new QAction(i18n("&Decimal"), ag); act->setData(Variable::Decimal); act->setShortcut(Qt::Key_D); m_formatMenu->addAction(act); act = new QAction(i18n("&Hexadecimal"), ag); act->setData(Variable::Hexadecimal); act->setShortcut(Qt::Key_H); m_formatMenu->addAction(act); foreach(QAction* act, m_formatMenu->actions()) { act->setCheckable(true); act->setShortcutContext(Qt::WidgetWithChildrenShortcut); m_signalMapper->setMapping(act, act->data().toInt()); connect(act, &QAction::triggered, m_signalMapper, static_cast(&QSignalMapper::map)); addAction(act); } connect(m_signalMapper, static_cast(&QSignalMapper::mapped), this, &VariableTree::changeVariableFormat); m_watchDelete = new QAction( QIcon::fromTheme("edit-delete"), i18n( "Remove Watch Variable" ), this); m_watchDelete->setShortcut(Qt::Key_Delete); m_watchDelete->setShortcutContext(Qt::WidgetWithChildrenShortcut); addAction(m_watchDelete); connect(m_watchDelete, &QAction::triggered, this, &VariableTree::watchDelete); m_copyVariableValue = new QAction(i18n("&Copy Value"), this); m_copyVariableValue->setShortcutContext(Qt::WidgetWithChildrenShortcut); m_copyVariableValue->setShortcut(QKeySequence::Copy); connect(m_copyVariableValue, &QAction::triggered, this, &VariableTree::copyVariableValue); m_stopOnChange = new QAction(i18n("&Stop on Change"), this); connect(m_stopOnChange, &QAction::triggered, this, &VariableTree::stopOnChange); } Variable* VariableTree::selectedVariable() const { if (selectionModel()->selectedRows().isEmpty()) return 0; TreeItem* item = collection()->itemForIndex(selectionModel()->selectedRows().first()); if (!item) return 0; return dynamic_cast(item); } void VariableTree::contextMenuEvent(QContextMenuEvent* event) { if (!selectedVariable()) return; // set up menu QMenu contextMenu(this->parentWidget()); m_contextMenuTitle->setText(selectedVariable()->expression()); contextMenu.addAction(m_contextMenuTitle); if(selectedVariable()->canSetFormat()) contextMenu.addMenu(m_formatMenu); foreach(QAction* act, m_formatMenu->actions()) { if(act->data().toInt()==selectedVariable()->format()) act->setChecked(true); } if (dynamic_cast(selectedVariable()->parent())) { contextMenu.addAction(m_watchDelete); } contextMenu.addSeparator(); contextMenu.addAction(m_copyVariableValue); contextMenu.addAction(m_stopOnChange); contextMenu.exec(event->globalPos()); } void VariableTree::changeVariableFormat(int format) { if (!selectedVariable()) return; selectedVariable()->setFormat(static_cast(format)); } void VariableTree::watchDelete() { if (!selectedVariable()) return; if (!dynamic_cast(selectedVariable()->parent())) return; selectedVariable()->die(); } void VariableTree::copyVariableValue() { if (!selectedVariable()) return; QApplication::clipboard()->setText(selectedVariable()->value()); } void VariableTree::stopOnChange() { if (!selectedVariable()) return; IDebugSession *session = ICore::self()->debugController()->currentSession(); if (session && session->state() != IDebugSession::NotStartedState && session->state() != IDebugSession::EndedState) { session->variableController()->addWatchpoint(selectedVariable()); } } #if 0 void VariableTree::contextMenuEvent(QContextMenuEvent* event) { QModelIndex index = indexAt(event->pos()); if (!index.isValid()) return; AbstractVariableItem* item = collection()->itemForIndex(index); if (RecentItem* recent = qobject_cast(item)) { QMenu popup(this); popup.addTitle(i18n("Recent Expressions")); QAction* remove = popup.addAction(QIcon::fromTheme("editdelete"), i18n("Remove All")); QAction* reevaluate = popup.addAction(QIcon::fromTheme("reload"), i18n("Re-evaluate All")); if (controller()->stateIsOn(s_dbgNotStarted)) reevaluate->setEnabled(false); QAction* res = popup.exec(QCursor::pos()); if (res == remove) { collection()->deleteItem(recent); } else if (res == reevaluate) { foreach (AbstractVariableItem* item, recent->children()) { if (VariableItem* variable = qobject_cast(item)) variable->updateValue(); } } } else { activePopup_ = new QMenu(this); QMenu format(this); QAction* remember = 0; QAction* remove = 0; QAction* reevaluate = 0; QAction* watch = 0; QAction* natural = 0; QAction* hex = 0; QAction* decimal = 0; QAction* character = 0; QAction* binary = 0; #define MAYBE_DISABLE(action) if (!var->isAlive()) action->setEnabled(false) VariableItem* var = qobject_cast(item); AbstractVariableItem* root = item->abstractRoot(); RecentItem* recentRoot = qobject_cast(root); if (!recentRoot) { remember = activePopup_->addAction(QIcon::fromTheme("draw-freehand"), i18n("Remember Value")); MAYBE_DISABLE(remember); } if (!recentRoot) { watch = activePopup_->addAction(i18n("Watch Variable")); MAYBE_DISABLE(watch); } if (recentRoot) { reevaluate = activePopup_->addAction(QIcon::fromTheme("reload"), i18n("Reevaluate Expression")); MAYBE_DISABLE(reevaluate); remove = activePopup_->addAction(QIcon::fromTheme("editdelete"), i18n("Remove Expression")); remove->setShortcut(Qt::Key_Delete); } if (var) { toggleWatch_ = activePopup_->addAction( i18n("Data write breakpoint") ); toggleWatch_->setCheckable(true); toggleWatch_->setEnabled(false); } /* This code can be executed when debugger is stopped, and we invoke popup menu on a var under "recent expressions" just to delete it. */ if (var && var->isAlive() && !controller()->stateIsOn(s_dbgNotStarted)) { GDBCommand* cmd = new GDBCommand(DataEvaluateExpression, QStringLiteral("&%1") .arg(var->gdbExpression())); cmd->setHandler(this, &VariableTree::handleAddressComputed, true /*handles error*/); cmd->setThread(var->thread()); cmd->setFrame(var->frame()); controller_->addCommand(cmd); } QAction* res = activePopup_->exec(event->globalPos()); delete activePopup_; activePopup_ = 0; if (res == remember) { if (var) { ((VariableWidget*)parent())-> slotEvaluateExpression(var->gdbExpression()); } } else if (res == watch) { if (var) { ((VariableWidget*)parent())-> slotAddWatchVariable(var->gdbExpression()); } } else if (res == remove) { delete item; } else if (res == toggleWatch_) { if (var) emit toggleWatchpoint(var->gdbExpression()); } else if (res == reevaluate) { if (var) { var->updateValue(); } } event->accept(); } } void VariableTree::updateCurrentFrame() { } // ************************************************************************** void VariableTree::handleAddressComputed(const GDBMI::ResultRecord& r) { if (r.reason == "error") { // Not lvalue, leave item disabled. return; } if (activePopup_) { toggleWatch_->setEnabled(true); //quint64 address = r["value"].literal().toULongLong(0, 16); /*if (breakpointWidget_->hasWatchpointForAddress(address)) { toggleWatch_->setChecked(true); }*/ } } VariableCollection * VariableTree::collection() const { return controller_->variables(); } GDBController * VariableTree::controller() const { return controller_; } void VariableTree::showEvent(QShowEvent * event) { Q_UNUSED(event) for (int i = 0; i < model()->columnCount(); ++i) resizeColumnToContents(i); } #endif // ************************************************************************** // ************************************************************************** // ************************************************************************** } diff --git a/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp b/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp index d78b5a0d72..8ee167dc49 100644 --- a/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp +++ b/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp @@ -1,725 +1,724 @@ /* Copyright 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "abstractdeclarationnavigationcontext.h" #include #include #include "../functiondeclaration.h" #include "../functiondefinition.h" #include "../classfunctiondeclaration.h" #include "../namespacealiasdeclaration.h" #include "../forwarddeclaration.h" #include "../types/enumeratortype.h" #include "../types/enumerationtype.h" #include "../types/functiontype.h" #include "../duchainutils.h" #include "../types/pointertype.h" #include "../types/referencetype.h" #include "../types/typeutils.h" #include "../types/typesystem.h" #include "../persistentsymboltable.h" #include "util/debug.h" #include #include #include #include #include -#include namespace KDevelop { AbstractDeclarationNavigationContext::AbstractDeclarationNavigationContext( DeclarationPointer decl, KDevelop::TopDUContextPointer topContext, AbstractNavigationContext* previousContext) : AbstractNavigationContext((topContext ? topContext : TopDUContextPointer(decl ? decl->topContext() : 0)), previousContext), m_declaration(decl), m_fullBackwardSearch(false) { //Jump from definition to declaration if possible FunctionDefinition* definition = dynamic_cast(m_declaration.data()); if(definition && definition->declaration()) m_declaration = DeclarationPointer(definition->declaration()); } QString AbstractDeclarationNavigationContext::name() const { if(m_declaration.data()) return prettyQualifiedIdentifier(m_declaration).toString(); else return declarationName(m_declaration); } QString AbstractDeclarationNavigationContext::html(bool shorten) { clear(); m_shorten = shorten; modifyHtml() += "

" + fontSizePrefix(shorten); addExternalHtml(m_prefix); if(!m_declaration.data()) { modifyHtml() += i18n("
lost declaration
"); return currentHtml(); } if( m_previousContext ) { QString link = createLink( m_previousContext->name(), m_previousContext->name(), NavigationAction(m_previousContext) ); modifyHtml() += navigationHighlight(i18n("Back to %1
", link)); } QExplicitlySharedDataPointer doc; if( !shorten ) { doc = ICore::self()->documentationController()->documentationForDeclaration(m_declaration.data()); const AbstractFunctionDeclaration* function = dynamic_cast(m_declaration.data()); if( function ) { htmlFunction(); } else if( m_declaration->isTypeAlias() || m_declaration->type() || m_declaration->kind() == Declaration::Instance ) { if( m_declaration->isTypeAlias() ) modifyHtml() += importantHighlight("typedef "); if(m_declaration->type()) modifyHtml() += i18n("enumerator "); AbstractType::Ptr useType = m_declaration->abstractType(); if(m_declaration->isTypeAlias()) { //Do not show the own name as type of typedefs if(useType.cast()) useType = useType.cast()->type(); } eventuallyMakeTypeLinks( useType ); modifyHtml() += ' ' + identifierHighlight(declarationName(m_declaration).toHtmlEscaped(), m_declaration); if(auto integralType = m_declaration->type()) { const QString plainValue = integralType->valueAsString(); if (!plainValue.isEmpty()) { modifyHtml() += QStringLiteral(" = ") + plainValue; } } modifyHtml() += "
"; }else{ if( m_declaration->kind() == Declaration::Type && m_declaration->abstractType().cast() ) { htmlClass(); } if ( m_declaration->kind() == Declaration::Namespace ) { modifyHtml() += i18n("namespace %1 ", identifierHighlight(m_declaration->qualifiedIdentifier().toString().toHtmlEscaped(), m_declaration)); } if(m_declaration->type()) { EnumerationType::Ptr enumeration = m_declaration->type(); modifyHtml() += i18n("enumeration %1 ", identifierHighlight(m_declaration->identifier().toString().toHtmlEscaped(), m_declaration)); } if(m_declaration->isForwardDeclaration()) { ForwardDeclaration* forwardDec = static_cast(m_declaration.data()); Declaration* resolved = forwardDec->resolve(m_topContext.data()); if(resolved) { modifyHtml() += i18n("(resolved forward-declaration: "); makeLink(resolved->identifier().toString(), KDevelop::DeclarationPointer(resolved), NavigationAction::NavigateDeclaration ); modifyHtml() += i18n(") "); }else{ modifyHtml() += i18n("(unresolved forward-declaration) "); QualifiedIdentifier id = forwardDec->qualifiedIdentifier(); uint count; const IndexedDeclaration* decls; PersistentSymbolTable::self().declarations(id, count, decls); for(uint a = 0; a < count; ++a) { if(decls[a].isValid() && !decls[a].data()->isForwardDeclaration()) { modifyHtml() += "
"; makeLink(i18n("possible resolution from"), KDevelop::DeclarationPointer(decls[a].data()), NavigationAction::NavigateDeclaration); modifyHtml() += ' ' + decls[a].data()->url().str(); } } } } modifyHtml() += "
"; } }else{ AbstractType::Ptr showType = m_declaration->abstractType(); if(showType && showType.cast()) { showType = showType.cast()->returnType(); if(showType) modifyHtml() += labelHighlight(i18n("Returns: ")); }else if(showType) { modifyHtml() += labelHighlight(i18n("Type: ")); } if(showType) { eventuallyMakeTypeLinks(showType); modifyHtml() += " "; } } QualifiedIdentifier identifier = m_declaration->qualifiedIdentifier(); if( identifier.count() > 1 ) { if( m_declaration->context() && m_declaration->context()->owner() ) { Declaration* decl = m_declaration->context()->owner(); FunctionDefinition* definition = dynamic_cast(decl); if(definition && definition->declaration()) decl = definition->declaration(); if(decl->abstractType().cast()) modifyHtml() += labelHighlight(i18n("Enum: ")); else modifyHtml() += labelHighlight(i18n("Container: ")); makeLink( declarationName(DeclarationPointer(decl)), DeclarationPointer(decl), NavigationAction::NavigateDeclaration ); modifyHtml() += " "; } else { QualifiedIdentifier parent = identifier; parent.pop(); modifyHtml() += labelHighlight(i18n("Scope: %1 ", typeHighlight(parent.toString().toHtmlEscaped()))); } } if( shorten && !m_declaration->comment().isEmpty() ) { QString comment = QString::fromUtf8(m_declaration->comment()); if( comment.length() > 60 ) { comment.truncate(60); comment += "..."; } comment.replace('\n', " "); comment.replace("
", " "); comment.replace("
", " "); modifyHtml() += commentHighlight(comment.toHtmlEscaped()) + " "; } QString access = stringFromAccess(m_declaration); if( !access.isEmpty() ) modifyHtml() += labelHighlight(i18n("Access: %1 ", propertyHighlight(access.toHtmlEscaped()))); ///@todo Enumerations QString detailsHtml; QStringList details = declarationDetails(m_declaration); if( !details.isEmpty() ) { bool first = true; foreach( const QString &str, details ) { if( !first ) detailsHtml += ", "; first = false; detailsHtml += propertyHighlight(str); } } QString kind = declarationKind(m_declaration); if( !kind.isEmpty() ) { if( !detailsHtml.isEmpty() ) modifyHtml() += labelHighlight(i18n("Kind: %1 %2 ", importantHighlight(kind.toHtmlEscaped()), detailsHtml)); else modifyHtml() += labelHighlight(i18n("Kind: %1 ", importantHighlight(kind.toHtmlEscaped()))); } if (m_declaration->isDeprecated()) { modifyHtml() += labelHighlight(i18n("Status: %1 ", propertyHighlight(i18n("Deprecated")))); } modifyHtml() += "
"; if(!shorten) htmlAdditionalNavigation(); if( !shorten ) { if(dynamic_cast(m_declaration.data())) modifyHtml() += labelHighlight(i18n( "Def.: " )); else modifyHtml() += labelHighlight(i18n( "Decl.: " )); makeLink( QStringLiteral("%1 :%2").arg( m_declaration->url().toUrl().fileName() ).arg( m_declaration->rangeInCurrentRevision().start().line()+1 ), m_declaration, NavigationAction::JumpToSource ); modifyHtml() += " "; //modifyHtml() += "
"; if(!dynamic_cast(m_declaration.data())) { if( FunctionDefinition* definition = FunctionDefinition::definition(m_declaration.data()) ) { modifyHtml() += labelHighlight(i18n( " Def.: " )); makeLink( QStringLiteral("%1 :%2").arg( definition->url().toUrl().fileName() ).arg( definition->rangeInCurrentRevision().start().line()+1 ), DeclarationPointer(definition), NavigationAction::JumpToSource ); } } if( FunctionDefinition* definition = dynamic_cast(m_declaration.data()) ) { if(definition->declaration()) { modifyHtml() += labelHighlight(i18n( " Decl.: " )); makeLink( QStringLiteral("%1 :%2").arg( definition->declaration()->url().toUrl().fileName() ).arg( definition->declaration()->rangeInCurrentRevision().start().line()+1 ), DeclarationPointer(definition->declaration()), NavigationAction::JumpToSource ); } } modifyHtml() += " "; //The action name _must_ stay "show_uses", since that is also used from outside makeLink(i18n("Show uses"), "show_uses", NavigationAction(m_declaration, NavigationAction::NavigateUses)); } if( !shorten && (!m_declaration->comment().isEmpty() || doc) ) { modifyHtml() += "
"; QString comment = QString::fromUtf8(m_declaration->comment()); if(comment.isEmpty() && doc) { comment = doc->description(); if(!comment.isEmpty()) { connect(doc.data(), &IDocumentation::descriptionChanged, this, &AbstractDeclarationNavigationContext::contentsChanged); modifyHtml() += "
" + commentHighlight(comment); } } else if(!comment.isEmpty()) { // if the first paragraph does not contain a tag, we assume that this is a plain-text comment if (!Qt::mightBeRichText(comment)) { // still might contain extra html tags for line breaks (this is the case for doxygen-style comments sometimes) // let's protect them from being removed completely comment.replace(QRegExp("
"), "\n"); comment = comment.toHtmlEscaped(); comment.replace('\n', "
"); //Replicate newlines in html } modifyHtml() += commentHighlight(comment); modifyHtml() += "
"; } } if(!shorten && doc) { modifyHtml() += "
" + i18n("Show documentation for "); makeLink( prettyQualifiedIdentifier(m_declaration).toString(), m_declaration, NavigationAction::ShowDocumentation ); } //modifyHtml() += "
"; addExternalHtml(m_suffix); modifyHtml() += fontSizeSuffix(shorten) + "

"; return currentHtml(); } KDevelop::AbstractType::Ptr AbstractDeclarationNavigationContext::typeToShow(KDevelop::AbstractType::Ptr type) { return type; } void AbstractDeclarationNavigationContext::htmlFunction() { const AbstractFunctionDeclaration* function = dynamic_cast(m_declaration.data()); Q_ASSERT(function); const ClassFunctionDeclaration* classFunDecl = dynamic_cast(m_declaration.data()); const FunctionType::Ptr type = m_declaration->abstractType().cast(); if( !type ) { modifyHtml() += errorHighlight("Invalid type
"); return; } if( !classFunDecl || (!classFunDecl->isConstructor() && !classFunDecl->isDestructor()) ) { // only print return type for global functions and non-ctor/dtor methods eventuallyMakeTypeLinks( type->returnType() ); } modifyHtml() += ' ' + identifierHighlight(prettyIdentifier(m_declaration).toString().toHtmlEscaped(), m_declaration); if( type->indexedArgumentsSize() == 0 ) { modifyHtml() += "()"; } else { modifyHtml() += "( "; bool first = true; int firstDefaultParam = type->indexedArgumentsSize() - function->defaultParametersSize(); int currentArgNum = 0; QVector decls; if (KDevelop::DUContext* argumentContext = DUChainUtils::getArgumentContext(m_declaration.data())) { decls = argumentContext->localDeclarations(m_topContext.data()); } foreach(const AbstractType::Ptr& argType, type->arguments()) { if( !first ) modifyHtml() += ", "; first = false; eventuallyMakeTypeLinks( argType ); if (currentArgNum < decls.size()) { modifyHtml() += ' ' + identifierHighlight(decls[currentArgNum]->identifier().toString().toHtmlEscaped(), m_declaration); } if( currentArgNum >= firstDefaultParam ) modifyHtml() += " = " + function->defaultParameters()[ currentArgNum - firstDefaultParam ].str().toHtmlEscaped(); ++currentArgNum; } modifyHtml() += " )"; } modifyHtml() += "
"; } Identifier AbstractDeclarationNavigationContext::prettyIdentifier(DeclarationPointer decl) const { Identifier ret; QualifiedIdentifier q = prettyQualifiedIdentifier(decl); if(!q.isEmpty()) ret = q.last(); return ret; } QualifiedIdentifier AbstractDeclarationNavigationContext::prettyQualifiedIdentifier(DeclarationPointer decl) const { if(decl) return decl->qualifiedIdentifier(); else return QualifiedIdentifier(); } void AbstractDeclarationNavigationContext::htmlAdditionalNavigation() { ///Check if the function overrides or hides another one const ClassFunctionDeclaration* classFunDecl = dynamic_cast(m_declaration.data()); if(classFunDecl) { Declaration* overridden = DUChainUtils::getOverridden(m_declaration.data()); if(overridden) { modifyHtml() += i18n("Overrides a "); makeLink(i18n("function"), QStringLiteral("jump_to_overridden"), NavigationAction(DeclarationPointer(overridden), KDevelop::NavigationAction::NavigateDeclaration)); modifyHtml() += i18n(" from "); makeLink(prettyQualifiedIdentifier(DeclarationPointer(overridden->context()->owner())).toString(), QStringLiteral("jump_to_overridden_container"), NavigationAction(DeclarationPointer(overridden->context()->owner()), KDevelop::NavigationAction::NavigateDeclaration)); modifyHtml() += "
"; }else{ //Check if this declarations hides other declarations QList decls; foreach(const DUContext::Import &import, m_declaration->context()->importedParentContexts()) if(import.context(m_topContext.data())) decls += import.context(m_topContext.data())->findDeclarations(QualifiedIdentifier(m_declaration->identifier()), CursorInRevision::invalid(), AbstractType::Ptr(), m_topContext.data(), DUContext::DontSearchInParent); uint num = 0; foreach(Declaration* decl, decls) { modifyHtml() += i18n("Hides a "); makeLink(i18n("function"), QStringLiteral("jump_to_hide_%1").arg(num), NavigationAction(DeclarationPointer(decl), KDevelop::NavigationAction::NavigateDeclaration)); modifyHtml() += i18n(" from "); makeLink(prettyQualifiedIdentifier(DeclarationPointer(decl->context()->owner())).toString(), QStringLiteral("jump_to_hide_container_%1").arg(num), NavigationAction(DeclarationPointer(decl->context()->owner()), KDevelop::NavigationAction::NavigateDeclaration)); modifyHtml() += "
"; ++num; } } ///Show all places where this function is overridden if(classFunDecl->isVirtual()) { Declaration* classDecl = m_declaration->context()->owner(); if(classDecl) { uint maxAllowedSteps = m_fullBackwardSearch ? (uint)-1 : 10; QList overriders = DUChainUtils::getOverriders(classDecl, classFunDecl, maxAllowedSteps); if(!overriders.isEmpty()) { modifyHtml() += i18n("Overridden in "); bool first = true; foreach(Declaration* overrider, overriders) { if(!first) modifyHtml() += ", "; first = false; QString name = prettyQualifiedIdentifier(DeclarationPointer(overrider->context()->owner())).toString(); makeLink(name, name, NavigationAction(DeclarationPointer(overrider), NavigationAction::NavigateDeclaration)); } modifyHtml() += "
"; } if(maxAllowedSteps == 0) createFullBackwardSearchLink(overriders.isEmpty() ? i18n("Overriders possible, show all") : i18n("More overriders possible, show all")); } } } ///Show all classes that inherit this one uint maxAllowedSteps = m_fullBackwardSearch ? (uint)-1 : 10; QList inheriters = DUChainUtils::getInheriters(m_declaration.data(), maxAllowedSteps); if(!inheriters.isEmpty()) { modifyHtml() += i18n("Inherited by "); bool first = true; foreach(Declaration* importer, inheriters) { if(!first) modifyHtml() += ", "; first = false; QString importerName = prettyQualifiedIdentifier(DeclarationPointer(importer)).toString(); makeLink(importerName, importerName, NavigationAction(DeclarationPointer(importer), KDevelop::NavigationAction::NavigateDeclaration)); } modifyHtml() += "
"; } if(maxAllowedSteps == 0) createFullBackwardSearchLink(inheriters.isEmpty() ? i18n("Inheriters possible, show all") : i18n("More inheriters possible, show all")); } void AbstractDeclarationNavigationContext::createFullBackwardSearchLink(QString string) { makeLink(string, "m_fullBackwardSearch=true", NavigationAction("m_fullBackwardSearch=true")); modifyHtml() += "
"; } NavigationContextPointer AbstractDeclarationNavigationContext::executeKeyAction( QString key ) { if(key == "m_fullBackwardSearch=true") { m_fullBackwardSearch = true; clear(); } return NavigationContextPointer(this); } void AbstractDeclarationNavigationContext::htmlClass() { StructureType::Ptr klass = m_declaration->abstractType().cast(); Q_ASSERT(klass); ClassDeclaration* classDecl = dynamic_cast(klass->declaration(m_topContext.data())); if(classDecl) { switch ( classDecl->classType() ) { case ClassDeclarationData::Class: modifyHtml() += "class "; break; case ClassDeclarationData::Struct: modifyHtml() += "struct "; break; case ClassDeclarationData::Union: modifyHtml() += "union "; break; case ClassDeclarationData::Interface: modifyHtml() += "interface "; break; case ClassDeclarationData::Trait: modifyHtml() += "trait "; break; default: modifyHtml() += " "; break; } eventuallyMakeTypeLinks( klass.cast() ); FOREACH_FUNCTION( const KDevelop::BaseClassInstance& base, classDecl->baseClasses ) { modifyHtml() += ", " + stringFromAccess(base.access) + " " + (base.virtualInheritance ? QStringLiteral("virtual") : QString()) + " "; eventuallyMakeTypeLinks(base.baseClass.abstractType()); } } else { /// @todo How can we get here? and should this really be a class? modifyHtml() += "class "; eventuallyMakeTypeLinks( klass.cast() ); } modifyHtml() += " "; } void AbstractDeclarationNavigationContext::htmlIdentifiedType(AbstractType::Ptr type, const IdentifiedType* idType) { Q_ASSERT(type); Q_ASSERT(idType); if( Declaration* decl = idType->declaration(m_topContext.data()) ) { //Remove the last template-identifiers, because we create those directly QualifiedIdentifier id = prettyQualifiedIdentifier(DeclarationPointer(decl)); Identifier lastId = id.last(); id.pop(); lastId.clearTemplateIdentifiers(); id.push(lastId); if(decl->context() && decl->context()->owner()) { //Also create full type-links for the context around AbstractType::Ptr contextType = decl->context()->owner()->abstractType(); IdentifiedType* contextIdType = dynamic_cast(contextType.data()); if(contextIdType && !contextIdType->equals(idType)) { //Create full type information for the context if(!id.isEmpty()) id = id.mid(id.count()-1); htmlIdentifiedType(contextType, contextIdType); modifyHtml() += QStringLiteral("::").toHtmlEscaped(); } } //We leave out the * and & reference and pointer signs, those are added to the end makeLink(id.toString() , DeclarationPointer(idType->declaration(m_topContext.data())), NavigationAction::NavigateDeclaration ); } else { qCDebug(LANGUAGE) << "could not resolve declaration:" << idType->declarationId().isDirect() << idType->qualifiedIdentifier().toString() << "in top-context" << m_topContext->url().str(); modifyHtml() += typeHighlight(type->toString().toHtmlEscaped()); } } void AbstractDeclarationNavigationContext::eventuallyMakeTypeLinks( AbstractType::Ptr type ) { type = typeToShow(type); if( !type ) { modifyHtml() += typeHighlight(QStringLiteral("").toHtmlEscaped()); return; } AbstractType::Ptr target = TypeUtils::targetTypeKeepAliases( type, m_topContext.data() ); const IdentifiedType* idType = dynamic_cast( target.data() ); - qCDebug(LANGUAGE) << "making type-links for" << type->toString() << typeid(*type).name(); + qCDebug(LANGUAGE) << "making type-links for" << type->toString(); if( idType && idType->declaration(m_topContext.data()) ) { ///@todo This is C++ specific, move into subclass if(target->modifiers() & AbstractType::ConstModifier) modifyHtml() += typeHighlight("const "); htmlIdentifiedType(target, idType); //We need to exchange the target type, else template-parameters may confuse this SimpleTypeExchanger exchangeTarget(target, AbstractType::Ptr()); AbstractType::Ptr exchanged = exchangeTarget.exchange(type); if(exchanged) { QString typeSuffixString = exchanged->toString(); QRegExp suffixExp("\\&|\\*"); int suffixPos = typeSuffixString.indexOf(suffixExp); if(suffixPos != -1) modifyHtml() += typeHighlight(typeSuffixString.mid(suffixPos)); } } else { if(idType) { qCDebug(LANGUAGE) << "identified type could not be resolved:" << idType->qualifiedIdentifier() << idType->declarationId().isValid() << idType->declarationId().isDirect(); } modifyHtml() += typeHighlight(type->toString().toHtmlEscaped()); } } DeclarationPointer AbstractDeclarationNavigationContext::declaration() const { return m_declaration; } QString AbstractDeclarationNavigationContext::identifierHighlight(const QString& identifier, const DeclarationPointer& decl) const { QString ret = nameHighlight(identifier); if (!decl) { return ret; } if (decl->isDeprecated()) { ret = QStringLiteral("") + ret + QStringLiteral(""); } return ret; } QString AbstractDeclarationNavigationContext::stringFromAccess(Declaration::AccessPolicy access) { switch(access) { case Declaration::Private: return "private"; case Declaration::Protected: return "protected"; case Declaration::Public: return "public"; default: break; } return ""; } QString AbstractDeclarationNavigationContext::stringFromAccess(DeclarationPointer decl) { const ClassMemberDeclaration* memberDecl = dynamic_cast(decl.data()); if( memberDecl ) { return stringFromAccess(memberDecl->accessPolicy()); } return QString(); } QString AbstractDeclarationNavigationContext::declarationName( DeclarationPointer decl ) const { if( NamespaceAliasDeclaration* alias = dynamic_cast(decl.data()) ) { if( alias->identifier().isEmpty() ) return "using namespace " + alias->importIdentifier().toString(); else return "namespace " + alias->identifier().toString() + " = " + alias->importIdentifier().toString(); } if( !decl ) return i18nc("A declaration that is unknown", "Unknown"); else return prettyIdentifier(decl).toString(); } QStringList AbstractDeclarationNavigationContext::declarationDetails(DeclarationPointer decl) { QStringList details; const AbstractFunctionDeclaration* function = dynamic_cast(decl.data()); const ClassMemberDeclaration* memberDecl = dynamic_cast(decl.data()); if( memberDecl ) { if( memberDecl->isMutable() ) details << "mutable"; if( memberDecl->isRegister() ) details << "register"; if( memberDecl->isStatic() ) details << "static"; if( memberDecl->isAuto() ) details << "auto"; if( memberDecl->isExtern() ) details << "extern"; if( memberDecl->isFriend() ) details << "friend"; } if( decl->isDefinition() ) details << i18nc("tells if a declaration is defining the variable's value", "definition"); if( decl->isExplicitlyDeleted() ) details << "deleted"; if( memberDecl && memberDecl->isForwardDeclaration() ) details << i18nc("as in c++ forward declaration", "forward"); AbstractType::Ptr t(decl->abstractType()); if( t ) { if( t->modifiers() & AbstractType::ConstModifier ) details << i18nc("a variable that won't change, const", "constant"); if( t->modifiers() & AbstractType::VolatileModifier ) details << "volatile"; } if( function ) { if( function->isInline() ) details << "inline"; if( function->isExplicit() ) details << "explicit"; if( function->isVirtual() ) details << "virtual"; const ClassFunctionDeclaration* classFunDecl = dynamic_cast(decl.data()); if( classFunDecl ) { if( classFunDecl->isSignal() ) details << "signal"; if( classFunDecl->isSlot() ) details << "slot"; if( classFunDecl->isConstructor() ) details << "constructor"; if( classFunDecl->isDestructor() ) details << "destructor"; if( classFunDecl->isConversionFunction() ) details << "conversion-function"; if( classFunDecl->isAbstract() ) details << "abstract"; } } return details; } } diff --git a/language/duchain/types/typesystem.h b/language/duchain/types/typesystem.h index a054882bcf..392548296a 100644 --- a/language/duchain/types/typesystem.h +++ b/language/duchain/types/typesystem.h @@ -1,129 +1,130 @@ /* This file is part of KDevelop Copyright 2006 Roberto Raggi Copyright 2006-2008 Hamish Rodda Copyright 2007-2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_TYPESYSTEM_H #define KDEVPLATFORM_TYPESYSTEM_H #include #include #include "typepointer.h" #include "../identifier.h" #include "abstracttype.h" namespace KDevelop { class AbstractTypeDataRequest; class AbstractType; class IntegralType; class PointerType; class ReferenceType; class FunctionType; class StructureType; class ArrayType; class TypeExchanger; class KDEVPLATFORMLANGUAGE_EXPORT TypeVisitor { public: virtual ~TypeVisitor (); virtual bool preVisit (const AbstractType *) = 0; virtual void postVisit (const AbstractType *) = 0; ///Return whether sub-types should be visited(same for the other visit functions) virtual bool visit(const AbstractType*) = 0; virtual void visit (const IntegralType *) = 0; virtual bool visit (const PointerType *) = 0; virtual void endVisit (const PointerType *) = 0; virtual bool visit (const ReferenceType *) = 0; virtual void endVisit (const ReferenceType *) = 0; virtual bool visit (const FunctionType *) = 0; virtual void endVisit (const FunctionType *) = 0; virtual bool visit (const StructureType *) = 0; virtual void endVisit (const StructureType *) = 0; virtual bool visit (const ArrayType *) = 0; virtual void endVisit (const ArrayType *) = 0; }; class KDEVPLATFORMLANGUAGE_EXPORT SimpleTypeVisitor : public TypeVisitor { public: - ///When using SimpleTypeVisitor, this is the only function you must override to collect all types. - virtual bool visit(const AbstractType*) = 0; + /// When using SimpleTypeVisitor, the visit taking an AbstractType is the only function + /// you must override to collect all types. + using TypeVisitor::visit; virtual bool preVisit (const AbstractType *) override ; virtual void postVisit (const AbstractType *) override ; virtual void visit (const IntegralType *) override ; virtual bool visit (const PointerType *) override ; virtual void endVisit (const PointerType *) override ; virtual bool visit (const ReferenceType *) override ; virtual void endVisit (const ReferenceType *) override ; virtual bool visit (const FunctionType *) override ; virtual void endVisit (const FunctionType *) override ; virtual bool visit (const StructureType *) override ; virtual void endVisit (const StructureType *) override ; virtual bool visit (const ArrayType *) override ; virtual void endVisit (const ArrayType *) override ; }; /** * A class that can be used to walk through all types that are references from one type, and exchange them with other types. * Examples for such types: Base-classes of a class, function-argument types of a function, etc. * */ class KDEVPLATFORMLANGUAGE_EXPORT TypeExchanger { public: virtual ~TypeExchanger() { } /** * By default should return the given type, and can return another type that the given should be replaced with. * Types should allow replacing all their held types using this from within their exchangeTypes function. * The default-implementation recurses the exchange, so should be called from within the derived implementation if that is wished. * */ virtual AbstractType::Ptr exchange( const AbstractType::Ptr& ); }; ///A simple type-exchanger that replaces one type with another class KDEVPLATFORMLANGUAGE_EXPORT SimpleTypeExchanger : public TypeExchanger { public: SimpleTypeExchanger(AbstractType::Ptr replace, AbstractType::Ptr replaceWith); virtual AbstractType::Ptr exchange( const AbstractType::Ptr& ) override ; private: AbstractType::Ptr m_replace, m_replaceWith; }; } #endif // KDEVPLATFORM_TYPESYSTEM_H diff --git a/plugins/openwith/openwithplugin.cpp b/plugins/openwith/openwithplugin.cpp index 4a397395bf..d55fa451e5 100644 --- a/plugins/openwith/openwithplugin.cpp +++ b/plugins/openwith/openwithplugin.cpp @@ -1,278 +1,278 @@ /* * This file is part of KDevelop * Copyright 2009 Andreas Pakulat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library 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 "openwithplugin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; K_PLUGIN_FACTORY_WITH_JSON(KDevOpenWithFactory, "kdevopenwith.json", registerPlugin();) namespace { bool sortActions(QAction* left, QAction* right) { return left->text() < right->text(); } bool isTextEditor(const KService::Ptr& service) { return service->serviceTypes().contains( "KTextEditor/Document" ); } QString defaultForMimeType(const QString& mimeType) { KConfigGroup config = KSharedConfig::openConfig()->group("Open With Defaults"); if (config.hasKey(mimeType)) { QString storageId = config.readEntry(mimeType, QString()); if (!storageId.isEmpty() && KService::serviceByStorageId(storageId)) { return storageId; } } return QString(); } bool canOpenDefault(const QString& mimeType) { if (defaultForMimeType(mimeType).isEmpty() && mimeType == "inode/directory") { // potentially happens in non-kde environments apparently, see https://git.reviewboard.kde.org/r/122373 return KMimeTypeTrader::self()->preferredService(mimeType); } else { return true; } } } OpenWithPlugin::OpenWithPlugin ( QObject* parent, const QVariantList& ) : IPlugin ( "kdevopenwith", parent ), m_actionMap( 0 ) { KDEV_USE_EXTENSION_INTERFACE( IOpenWith ) } OpenWithPlugin::~OpenWithPlugin() { } KDevelop::ContextMenuExtension OpenWithPlugin::contextMenuExtension( KDevelop::Context* context ) { // do not recurse if (context->hasType(KDevelop::Context::OpenWithContext)) { return ContextMenuExtension(); } m_urls.clear(); m_actionMap.reset(); m_services.clear(); FileContext* filectx = dynamic_cast( context ); ProjectItemContext* projctx = dynamic_cast( context ); if ( filectx && filectx->urls().count() > 0 ) { m_urls = filectx->urls(); } else if ( projctx && projctx->items().count() > 0 ) { // For now, let's handle *either* files only *or* directories only const int wantedType = projctx->items().first()->type(); foreach( ProjectBaseItem* item, projctx->items() ) { if (wantedType == ProjectBaseItem::File && item->file()) { m_urls << item->file()->path().toUrl(); } else if ((wantedType == ProjectBaseItem::Folder || wantedType == ProjectBaseItem::BuildFolder) && item->folder()) { m_urls << item->folder()->path().toUrl(); } } } if (m_urls.isEmpty()) { return KDevelop::ContextMenuExtension(); } m_actionMap.reset(new QSignalMapper( this )); connect( m_actionMap.data(), static_cast(&QSignalMapper::mapped), this, &OpenWithPlugin::open ); // Ok, lets fetch the mimetype for the !!first!! url and the relevant services // TODO: Think about possible alternatives to using the mimetype of the first url. QMimeType mimetype = QMimeDatabase().mimeTypeForUrl(m_urls.first()); m_mimeType = mimetype.name(); QList partActions = actionsForServiceType("KParts/ReadOnlyPart"); QList appActions = actionsForServiceType("Application"); OpenWithContext subContext(m_urls, mimetype); QList extensions = ICore::self()->pluginController()->queryPluginsForContextMenuExtensions( &subContext ); foreach( const ContextMenuExtension& ext, extensions ) { appActions += ext.actions(ContextMenuExtension::OpenExternalGroup); partActions += ext.actions(ContextMenuExtension::OpenEmbeddedGroup); } // Now setup a menu with actions for each part and app QMenu* menu = new QMenu( i18n("Open With" ) ); menu->setIcon( QIcon::fromTheme( "document-open" ) ); if (!partActions.isEmpty()) { menu->addSection(i18n("Embedded Editors")); menu->addActions( partActions ); } if (!appActions.isEmpty()) { menu->addSection(i18n("External Applications")); menu->addActions( appActions ); } KDevelop::ContextMenuExtension ext; if (canOpenDefault(m_mimeType)) { QAction* openAction = new QAction( i18n( "Open" ), this ); openAction->setIcon( QIcon::fromTheme( "document-open" ) ); connect( openAction, SIGNAL(triggered()), SLOT(openDefault()) ); ext.addAction( KDevelop::ContextMenuExtension::FileGroup, openAction ); } if (!menu->isEmpty()) { ext.addAction(KDevelop::ContextMenuExtension::FileGroup, menu->menuAction()); } else { delete menu; } return ext; } QList OpenWithPlugin::actionsForServiceType( const QString& serviceType ) { KService::List list = KMimeTypeTrader::self()->query( m_mimeType, serviceType ); KService::Ptr pref = KMimeTypeTrader::self()->preferredService( m_mimeType, serviceType ); m_services += list; QList actions; QAction* standardAction = 0; const QString defaultId = defaultForMimeType(m_mimeType); foreach( KService::Ptr svc, list ) { QAction* act = new QAction( isTextEditor(svc) ? i18n("Default Editor") : svc->name(), this ); act->setIcon( QIcon::fromTheme( svc->icon() ) ); if (svc->storageId() == defaultId || (defaultId.isEmpty() && isTextEditor(svc))) { QFont font = act->font(); font.setBold(true); act->setFont(font); } connect(act, &QAction::triggered, m_actionMap.data(), static_cast(&QSignalMapper::map)); m_actionMap->setMapping( act, svc->storageId() ); actions << act; if ( isTextEditor(svc) ) { standardAction = act; } else if ( svc->storageId() == pref->storageId() ) { standardAction = act; } } std::sort(actions.begin(), actions.end(), sortActions); if (standardAction) { actions.removeOne(standardAction); actions.prepend(standardAction); } return actions; } void OpenWithPlugin::openDefault() { // check preferred handler const QString defaultId = defaultForMimeType(m_mimeType); if (!defaultId.isEmpty()) { open(defaultId); return; } // default handlers if (m_mimeType == "inode/directory") { KService::Ptr service = KMimeTypeTrader::self()->preferredService(m_mimeType); - KRun::run(*service, m_urls, ICore::self()->uiController()->activeMainWindow()); + KRun::runService(*service, m_urls, ICore::self()->uiController()->activeMainWindow()); } else { foreach( const QUrl& u, m_urls ) { ICore::self()->documentController()->openDocument( u ); } } } void OpenWithPlugin::open( const QString& storageid ) { KService::Ptr svc = KService::serviceByStorageId( storageid ); if( svc->isApplication() ) { - KRun::run( *svc, m_urls, ICore::self()->uiController()->activeMainWindow() ); + KRun::runService( *svc, m_urls, ICore::self()->uiController()->activeMainWindow() ); } else { QString prefName = svc->desktopEntryName(); if ( isTextEditor(svc) ) { // If the user chose a KTE part, lets make sure we're creating a TextDocument instead of // a PartDocument by passing no preferredpart to the documentcontroller // TODO: Solve this rather inside DocumentController prefName = ""; } foreach( const QUrl& u, m_urls ) { ICore::self()->documentController()->openDocument( u, prefName ); } } KConfigGroup config = KSharedConfig::openConfig()->group("Open With Defaults"); if (storageid != config.readEntry(m_mimeType, QString())) { int setDefault = KMessageBox::questionYesNo( qApp->activeWindow(), i18nc("%1: mime type name, %2: app/part name", "Do you want to open all '%1' files by default with %2?", m_mimeType, svc->name() ), i18n("Set as default?"), KStandardGuiItem::yes(), KStandardGuiItem::no(), QStringLiteral("OpenWith-%1").arg(m_mimeType) ); if (setDefault == KMessageBox::Yes) { config.writeEntry(m_mimeType, storageid); } } } void OpenWithPlugin::openFilesInternal( const QList& files ) { if (files.isEmpty()) { return; } m_urls = files; m_mimeType = QMimeDatabase().mimeTypeForUrl(m_urls.first()).name(); openDefault(); } #include "openwithplugin.moc" diff --git a/project/projectconfigskeleton.cpp b/project/projectconfigskeleton.cpp index 7dd4dfa87d..0e0614996a 100644 --- a/project/projectconfigskeleton.cpp +++ b/project/projectconfigskeleton.cpp @@ -1,169 +1,169 @@ /* This file is part of KDevelop Copyright 2006 Adam Treat Copyright 2007 Andreas Pakulat This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "projectconfigskeleton.h" #include "debug.h" #include #include #include using namespace KDevelop; struct KDevelop::ProjectConfigSkeletonPrivate { QString m_developerTempFile; QString m_projectTempFile; Path m_projectFile; Path m_developerFile; bool mUseDefaults; }; ProjectConfigSkeleton::ProjectConfigSkeleton( const QString & configname ) : KConfigSkeleton( configname ), d( new ProjectConfigSkeletonPrivate ) { d->m_developerTempFile = configname; } ProjectConfigSkeleton::ProjectConfigSkeleton( KSharedConfigPtr config ) : KConfigSkeleton( config ), d( new ProjectConfigSkeletonPrivate ) { } void ProjectConfigSkeleton::setDeveloperTempFile( const QString& cfg ) { d->m_developerTempFile = cfg; setSharedConfig( KSharedConfig::openConfig( cfg ) ); } void ProjectConfigSkeleton::setProjectTempFile( const QString& cfg ) { d->m_projectTempFile = cfg; config()->addConfigSources( QStringList() << cfg ); - readConfig(); + load(); } void ProjectConfigSkeleton::setProjectFile( const Path& cfg ) { d->m_projectFile = cfg; } void ProjectConfigSkeleton::setDeveloperFile( const Path& cfg ) { d->m_developerFile = cfg; } Path ProjectConfigSkeleton::projectFile() const { return d->m_projectFile; } Path ProjectConfigSkeleton::developerFile() const { return d->m_developerFile; } void ProjectConfigSkeleton::setDefaults() { qCDebug(PROJECT) << "Setting Defaults"; KConfig cfg( d->m_projectTempFile ); Q_FOREACH( KConfigSkeletonItem* item, items() ) { item->swapDefault(); if( cfg.hasGroup( item->group() ) ) { KConfigGroup grp = cfg.group( item->group() ); if( grp.hasKey( item->key() ) ) item->setProperty( grp.readEntry( item->key(), item->property() ) ); } } } bool ProjectConfigSkeleton::useDefaults( bool b ) { if( b == d->mUseDefaults ) return d->mUseDefaults; if( b ) { KConfig cfg( d->m_projectTempFile ); Q_FOREACH( KConfigSkeletonItem* item, items() ) { item->swapDefault(); if( cfg.hasGroup( item->group() ) ) { qCDebug(PROJECT) << "reading"; KConfigGroup grp = cfg.group( item->group() ); if( grp.hasKey( item->key() ) ) item->setProperty( grp.readEntry( item->key(), item->property() ) ); } } } else { KConfig cfg( d->m_developerTempFile ); KConfig defCfg( d->m_projectTempFile ); Q_FOREACH( KConfigSkeletonItem* item, items() ) { if( cfg.hasGroup( item->group() ) ) { KConfigGroup grp = cfg.group( item->group() ); if( grp.hasKey( item->key() ) ) item->setProperty( grp.readEntry( item->key(), item->property() ) ); else { KConfigGroup grp = defCfg.group( item->group() ); item->setProperty( grp.readEntry( item->key(), item->property() ) ); } } else { KConfigGroup grp = defCfg.group( item->group() ); item->setProperty( grp.readEntry( item->key(), item->property() ) ); } } } d->mUseDefaults = b; return !d->mUseDefaults; } bool ProjectConfigSkeleton::writeConfig() { KConfigSkeletonItem::List myitems = items(); KConfigSkeletonItem::List::ConstIterator it; for( it = myitems.constBegin(); it != myitems.constEnd(); ++it ) { (*it)->writeConfig( config() ); } config()->sync(); - readConfig(); + load(); auto copyJob = KIO::copy(QUrl::fromLocalFile(d->m_developerTempFile), d->m_developerFile.toUrl()); copyJob ->exec(); emit configChanged(); return true; } ProjectConfigSkeleton::~ProjectConfigSkeleton() { delete d; }