File indexing completed on 2024-05-05 04:40:57

0001 /*
0002     SPDX-FileCopyrightText: 2006-2007 Andreas Pakulat <apaku@gmx.de>
0003     SPDX-FileCopyrightText: 2007 Dukju Ahn <dukjuahn@gmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "standardoutputview.h"
0009 #include "outputwidget.h"
0010 #include "toolviewdata.h"
0011 #include "debug.h"
0012 
0013 #include <QAbstractItemDelegate>
0014 #include <QAbstractItemModel>
0015 #include <QAction>
0016 #include <QList>
0017 
0018 #include <KLocalizedString>
0019 
0020 #include <interfaces/icore.h>
0021 #include <interfaces/iuicontroller.h>
0022 
0023 #include <sublime/view.h>
0024 #include <sublime/area.h>
0025 #include <sublime/controller.h>
0026 #include <sublime/document.h>
0027 
0028 class OutputViewFactory : public KDevelop::IToolViewFactory{
0029 public:
0030     explicit OutputViewFactory(const ToolViewData* data): m_data(data) {}
0031     QWidget* create(QWidget *parent = nullptr) override
0032     {
0033         return new OutputWidget( parent, m_data );
0034     }
0035     Qt::DockWidgetArea defaultPosition() const override
0036     {
0037         return Qt::BottomDockWidgetArea;
0038     }
0039     void viewCreated( Sublime::View* view ) override
0040     {
0041         m_data->views << view;
0042     }
0043     QString id() const override
0044     {
0045         //NOTE: id must be unique, see e.g. https://bugs.kde.org/show_bug.cgi?id=287093
0046         return QStringLiteral("org.kdevelop.OutputView.%1").arg(m_data->toolViewId);
0047     }
0048 private:
0049     const ToolViewData *m_data;
0050 };
0051 
0052 StandardOutputView::StandardOutputView(QObject *parent, const QVariantList &)
0053     : KDevelop::IPlugin(QStringLiteral("kdevstandardoutputview"), parent)
0054 {
0055     connect(KDevelop::ICore::self()->uiController()->controller(), &Sublime::Controller::aboutToRemoveView,
0056             this, &StandardOutputView::removeSublimeView);
0057 
0058 }
0059 
0060 void StandardOutputView::removeSublimeView( Sublime::View* v )
0061 {
0062     auto it = m_toolViews.begin();
0063     while (it != m_toolViews.end()) {
0064         ToolViewData* d = it.value();
0065         bool isErased = false;
0066         if( d->views.contains(v) )
0067         {
0068             if( d->views.count() == 1 )
0069             {
0070                 isErased = true;
0071                 it = m_toolViews.erase(it);
0072                 m_ids.removeAll( d->toolViewId );
0073                 delete d;
0074             } else
0075             {
0076                 d->views.removeAll(v);
0077             }
0078         }
0079         if (!isErased) {
0080             ++it;
0081         }
0082     }
0083 }
0084 
0085 StandardOutputView::~StandardOutputView()
0086 {
0087 }
0088 
0089 int StandardOutputView::standardToolView( KDevelop::IOutputView::StandardToolView view )
0090 {
0091     const auto standardViewIt = m_standardViews.constFind(view);
0092     if (standardViewIt != m_standardViews.constEnd()) {
0093         return *standardViewIt;
0094     }
0095 
0096     int ret = -1;
0097     switch( view )
0098     {
0099     case KDevelop::IOutputView::BuildView:
0100         ret = registerToolView(QByteArrayLiteral("Build"), i18nc("@title:window", "Build"),
0101                                KDevelop::IOutputView::HistoryView, QIcon::fromTheme(QStringLiteral("run-build")),
0102                                KDevelop::IOutputView::AddFilterAction);
0103         break;
0104     case KDevelop::IOutputView::RunView:
0105         ret = registerToolView(QByteArrayLiteral("Run"), i18nc("@title:window", "Run"),
0106                                KDevelop::IOutputView::MultipleView, QIcon::fromTheme(QStringLiteral("system-run")),
0107                                KDevelop::IOutputView::AddFilterAction);
0108         break;
0109     case KDevelop::IOutputView::DebugView:
0110         ret = registerToolView(QByteArrayLiteral("Debug"), i18nc("@title:window", "Debug"),
0111                                KDevelop::IOutputView::MultipleView, QIcon::fromTheme(QStringLiteral("debug-step-into")),
0112                                KDevelop::IOutputView::AddFilterAction);
0113         break;
0114     case KDevelop::IOutputView::TestView:
0115         // TODO: pass QByteArrayLiteral("Test") instead of QByteArray() and make the settings work.
0116         // The settings are disabled for the Test output tool view, because all Test output views are currently
0117         // unclosable and there is only one output tool view option right now: output view number limit, which
0118         // automatically closes its views and has no effect if the views are not closable.
0119         // AllowUserClose output view behavior is enabled by default, but 3 output jobs - CompileAnalyzeJob,
0120         // cppcheck::Job and Heaptrack::Job - disable it by calling `setBehaviours(KDevelop::IOutputView::AutoScroll);`.
0121         // 1cd05dd39bb67cbec7ce39a0237b64fb42e5fc95 introduced the first unclosable view in the cppcheck plugin with
0122         // no surviving explanation. Other Test output views followed the example later. Ideally all output views should
0123         // become closable, if there is no good reason to prevent closing and destroying them.
0124         ret = registerToolView(QByteArray(), i18nc("@title:window", "Test"), KDevelop::IOutputView::HistoryView,
0125                                QIcon::fromTheme(QStringLiteral("system-run")));
0126         break;
0127     case KDevelop::IOutputView::VcsView:
0128         ret = registerToolView(QByteArrayLiteral("VersionControl"), i18nc("@title:window", "Version Control"),
0129                                KDevelop::IOutputView::HistoryView, QIcon::fromTheme(QStringLiteral("system-run")));
0130         break;
0131     }
0132 
0133     Q_ASSERT(ret != -1);
0134     m_standardViews[view] = ret;
0135     return ret;
0136 }
0137 
0138 int StandardOutputView::registerToolView(const QByteArray& configSubgroupName, const QString& title,
0139                                          KDevelop::IOutputView::ViewType type, const QIcon& icon, Options option,
0140                                          const QList<QAction*>& actionList)
0141 {
0142     // try to reuse existing tool view
0143     for (ToolViewData* d : qAsConst(m_toolViews)) {
0144         if ( d->type == type && d->title == title ) {
0145             return d->toolViewId;
0146         }
0147     }
0148 
0149     // register new tool view
0150     const int newid = m_ids.isEmpty() ? 0 : (m_ids.last() + 1);
0151     qCDebug(PLUGIN_STANDARDOUTPUTVIEW) << "Registering view" << title << "with type:" << type << "id:" << newid;
0152     auto* tvdata = new ToolViewData( this );
0153     tvdata->toolViewId = newid;
0154     tvdata->configSubgroupName = configSubgroupName;
0155     tvdata->type = type;
0156     tvdata->title = title;
0157     tvdata->icon = icon;
0158     tvdata->plugin = this;
0159     tvdata->option = option;
0160     tvdata->actionList = actionList;
0161     core()->uiController()->addToolView( title, new OutputViewFactory( tvdata ) );
0162     m_ids << newid;
0163     m_toolViews[newid] = tvdata;
0164     return newid;
0165 }
0166 
0167 int StandardOutputView::registerOutputInToolView( int toolViewId,
0168                                                   const QString& title,
0169                                                   KDevelop::IOutputView::Behaviours behaviour )
0170 {
0171     const auto toolViewIt = m_toolViews.constFind(toolViewId);
0172     if (toolViewIt == m_toolViews.constEnd())
0173         return -1;
0174     int newid;
0175     if( m_ids.isEmpty() )
0176     {
0177         newid = 0;
0178     } else
0179     {
0180         newid = m_ids.last()+1;
0181     }
0182     m_ids << newid;
0183     (*toolViewIt)->addOutput(newid, title, behaviour);
0184     return newid;
0185 }
0186 
0187 void StandardOutputView::raiseOutput(int outputId)
0188 {
0189     for (const auto* toolViewData : qAsConst(m_toolViews))  {
0190         if (toolViewData->outputdata.contains(outputId)) {
0191             for (Sublime::View* v : qAsConst(toolViewData->views)) {
0192                 if( v->hasWidget() )
0193                 {
0194                     auto* w = qobject_cast<OutputWidget*>( v->widget() );
0195                     w->raiseOutput( outputId );
0196                     v->requestRaise();
0197                 }
0198             }
0199         }
0200         // TODO: not break here?
0201     }
0202 }
0203 
0204 void StandardOutputView::setModel( int outputId, QAbstractItemModel* model )
0205 {
0206     OutputData* outputData = nullptr;
0207     for (const auto* toolViewData : qAsConst(m_toolViews))  {
0208         const auto& outputDataMap = toolViewData->outputdata;
0209         auto outputDataIt = outputDataMap.find(outputId);
0210         if (outputDataIt != outputDataMap.end()) {
0211             outputData = outputDataIt.value();
0212             break;
0213         }
0214     }
0215     if (!outputData) {
0216         qCDebug(PLUGIN_STANDARDOUTPUTVIEW) << "Trying to set model on unknown view-id:" << outputId;
0217     } else {
0218         outputData->setModel(model);
0219     }
0220 }
0221 
0222 void StandardOutputView::setDelegate( int outputId, QAbstractItemDelegate* delegate )
0223 {
0224     OutputData* outputData = nullptr;
0225     for (const auto* toolViewData : qAsConst(m_toolViews))  {
0226         const auto& outputDataMap = toolViewData->outputdata;
0227         auto outputDataIt = outputDataMap.find(outputId);
0228         if (outputDataIt != outputDataMap.end()) {
0229             outputData = outputDataIt.value();
0230             break;
0231         }
0232     }
0233     if (!outputData) {
0234         qCDebug(PLUGIN_STANDARDOUTPUTVIEW) << "Trying to set model on unknown view-id:" << outputId;
0235     } else {
0236         outputData->setDelegate(delegate);
0237     }
0238 }
0239 
0240 void StandardOutputView::removeToolView(int toolViewId)
0241 {
0242     const auto toolViewIt = m_toolViews.find(toolViewId);
0243     if (toolViewIt != m_toolViews.end()) {
0244         ToolViewData* td = *toolViewIt;
0245         const auto views = td->views;
0246         for (Sublime::View* view : views) {
0247             if( view->hasWidget() )
0248             {
0249                 auto* outputWidget = qobject_cast<OutputWidget*>( view->widget() );
0250                 for (auto it = td->outputdata.keyBegin(), end = td->outputdata.keyEnd(); it != end; ++it) {
0251                     outputWidget->removeOutput(*it);
0252                 }
0253             }
0254             for (Sublime::Area* area : KDevelop::ICore::self()->uiController()->controller()->allAreas()) {
0255                 area->removeToolView( view );
0256             }
0257         }
0258         delete td;
0259         m_toolViews.erase(toolViewIt);
0260         emit toolViewRemoved(toolViewId);
0261     }
0262 }
0263 
0264 OutputWidget* StandardOutputView::outputWidgetForId( int outputId ) const
0265 {
0266     for (ToolViewData* td : m_toolViews) {
0267         if( td->outputdata.contains( outputId ) )
0268         {
0269             for (Sublime::View* view : qAsConst(td->views)) {
0270                 if( view->hasWidget() )
0271                     return qobject_cast<OutputWidget*>( view->widget() );
0272             }
0273         }
0274     }
0275     return nullptr;
0276 }
0277 
0278 void StandardOutputView::scrollOutputTo( int outputId, const QModelIndex& idx )
0279 {
0280     OutputWidget* widget = outputWidgetForId( outputId );
0281     if( widget )
0282         widget->scrollToIndex( idx );
0283 }
0284 
0285 void StandardOutputView::removeOutput( int outputId )
0286 {
0287     for (ToolViewData* td : qAsConst(m_toolViews)) {
0288         const auto outputIt = td->outputdata.find(outputId);
0289         if (outputIt != td->outputdata.end()) {
0290             for (Sublime::View* view : qAsConst(td->views)) {
0291                 if( view->hasWidget() )
0292                     qobject_cast<OutputWidget*>( view->widget() )->removeOutput( outputId );
0293             }
0294             td->outputdata.erase(outputIt);
0295         }
0296     }
0297 }
0298 
0299 void StandardOutputView::setTitle(int outputId, const QString& title)
0300 {
0301     OutputWidget* widget = outputWidgetForId(outputId);
0302     if (widget) {
0303         widget->setTitle(outputId, title);
0304     }
0305 }
0306 
0307 #include "moc_standardoutputview.cpp"