diff --git a/interfaces/CMakeLists.txt b/interfaces/CMakeLists.txt index 22553eaa40..b3c10fe429 100644 --- a/interfaces/CMakeLists.txt +++ b/interfaces/CMakeLists.txt @@ -1,114 +1,116 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kdevplatform\") set(KDevPlatformInterfaces_LIB_SRCS iassistant.cpp context.cpp configpage.cpp iplugin.cpp idocument.cpp icore.cpp iuicontroller.cpp iplugincontroller.cpp iprojectcontroller.cpp iproject.cpp ilanguagecontroller.cpp idocumentcontroller.cpp istatus.cpp iruncontroller.cpp isession.cpp isessionlock.cpp isourceformatter.cpp isourceformattercontroller.cpp contextmenuextension.cpp icompletionsettings.cpp iselectioncontroller.cpp idocumentationprovider.cpp idocumentationproviderprovider.cpp idocumentation.cpp idocumentationcontroller.cpp idebugcontroller.cpp ipartcontroller.cpp launchconfigurationpage.cpp launchconfigurationtype.cpp ilauncher.cpp ilaunchconfiguration.cpp ilaunchmode.cpp iprojectprovider.cpp ibuddydocumentfinder.cpp itemplateprovider.cpp itestsuite.cpp itestcontroller.cpp + itoolviewactionlistener.cpp ilanguagecheck.cpp ilanguagecheckprovider.cpp ) configure_file(ipluginversion.h.in ${CMAKE_CURRENT_BINARY_DIR}/ipluginversion.h) add_library(KDevPlatformInterfaces ${KDevPlatformInterfaces_LIB_SRCS}) add_library(KDev::Interfaces ALIAS KDevPlatformInterfaces) generate_export_header(KDevPlatformInterfaces EXPORT_FILE_NAME interfacesexport.h) target_link_libraries(KDevPlatformInterfaces LINK_PUBLIC KF5::ConfigCore KF5::Parts KF5::TextEditor KF5::I18n KF5::Service LINK_PRIVATE KF5::IconThemes KF5::NotifyConfig KF5::KIOWidgets KF5::ThreadWeaver ) target_include_directories(KDevPlatformInterfaces INTERFACE "$") set_target_properties(KDevPlatformInterfaces PROPERTIES VERSION ${KDEVPLATFORM_LIB_VERSION} SOVERSION ${KDEVPLATFORM_LIB_SOVERSION} EXPORT_NAME Interfaces) install(TARGETS KDevPlatformInterfaces EXPORT KDevPlatformTargets ${INSTALL_TARGETS_DEFAULT_ARGS} ) install(FILES iassistant.h context.h configpage.h contextmenuextension.h iplugin.h icore.h iuicontroller.h iplugincontroller.h iprojectcontroller.h iproject.h ilanguagecontroller.h idocument.h idocumentcontroller.h isourceformatter.h isourceformattercontroller.h istatus.h isession.h isessionlock.h iruncontroller.h ilaunchconfiguration.h ilauncher.h launchconfigurationpage.h launchconfigurationtype.h icompletionsettings.h iselectioncontroller.h idocumentation.h idocumentationprovider.h idocumentationproviderprovider.h idocumentationcontroller.h idebugcontroller.h ipartcontroller.h ilaunchmode.h iprojectprovider.h ilanguagecheck.h ilanguagecheckprovider.h ibuddydocumentfinder.h itemplateprovider.h itestsuite.h itestcontroller.h + itoolviewactionlistener.h ${CMAKE_CURRENT_BINARY_DIR}/interfacesexport.h ${CMAKE_CURRENT_BINARY_DIR}/ipluginversion.h DESTINATION ${INCLUDE_INSTALL_DIR}/kdevplatform/interfaces COMPONENT Devel ) install(FILES kdevelopplugin.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR} ) diff --git a/interfaces/itoolviewactionlistener.cpp b/interfaces/itoolviewactionlistener.cpp new file mode 100644 index 0000000000..2829c73607 --- /dev/null +++ b/interfaces/itoolviewactionlistener.cpp @@ -0,0 +1,26 @@ +/*************************************************************************** + * Copyright 2015 Kevin Funk * + * * + * 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 "itoolviewactionlistener.h" + +using namespace KDevelop; + +IToolViewActionListener::~IToolViewActionListener() +{ +} diff --git a/interfaces/itoolviewactionlistener.h b/interfaces/itoolviewactionlistener.h new file mode 100644 index 0000000000..0cf2b4d2de --- /dev/null +++ b/interfaces/itoolviewactionlistener.h @@ -0,0 +1,53 @@ +/*************************************************************************** + * Copyright 2015 Kevin Funk * + * * + * 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. * + ***************************************************************************/ + +#ifndef KDEVPLATFORM_ITOOLVIEWACTIONLISTENER_H +#define KDEVPLATFORM_ITOOLVIEWACTIONLISTENER_H + +#include "interfacesexport.h" + +#include + +namespace KDevelop { + +/** + * An interface for tool view widgets + * + * Implement this interface for the widgets which are being created by KDevelop::IToolViewFactory::create() + * + * This interface allows global shortcuts, such as "Jump to Next Output Mark", to map to the tool view widget + * which was last recently activated ("raised"). + * + * @sa KDevelop::IToolViewFactory::create() + */ +class KDEVPLATFORMINTERFACES_EXPORT IToolViewActionListener +{ +public: + virtual ~IToolViewActionListener(); + +public Q_SLOTS: + virtual void selectPreviousItem() = 0; + virtual void selectNextItem() = 0; +}; + +} + +Q_DECLARE_INTERFACE(KDevelop::IToolViewActionListener, "org.kdevelop.IToolViewActionListener") + +#endif diff --git a/interfaces/iuicontroller.h b/interfaces/iuicontroller.h index 8e17e0dcf2..d7f1d89463 100644 --- a/interfaces/iuicontroller.h +++ b/interfaces/iuicontroller.h @@ -1,168 +1,177 @@ /*************************************************************************** * Copyright 2007 Alexander Dymo * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_IUICONTROLLER_H #define KDEVPLATFORM_IUICONTROLLER_H #include "interfacesexport.h" #include #include class QAction; template class QExplicitlySharedDataPointer; namespace KParts { class MainWindow; } namespace Sublime{ class Controller; class View; class Area; } namespace KDevelop { class IDocument; class IAssistant; class KDEVPLATFORMINTERFACES_EXPORT IToolViewFactory { public: virtual ~IToolViewFactory() {} /** * called to create a new widget for this toolview * @param parent the parent to use as parent for the widget * @returns the new widget for the toolview */ virtual QWidget* create(QWidget *parent = 0) = 0; /** * @returns the identifier of this toolview. The identifier * is used to remember which areas the tool view should appear * in, and must never change. */ virtual QString id() const = 0; /** * @returns the default position where this toolview should appear */ virtual Qt::DockWidgetArea defaultPosition() = 0; /** * Fetch a list of actions to add to the toolbar of the toolview @p view * @param view the view to which the actions should be added * @returns a list of actions to be added to the toolbar */ virtual QList toolBarActions( QWidget* viewWidget ) const { return viewWidget->actions(); } /** * Fetch a list of actions to be shown in the context menu of the toolview @p view. * The default implementation will return all actions of @p viewWidget. * * @param view the view for which the context menu should be shown * @returns a list of actions to be shown in the context menu */ virtual QList contextMenuActions( QWidget* viewWidget ) const { return viewWidget->actions(); } /** * called when a new view is created from this template * @param view the new sublime view that is being shown */ virtual void viewCreated(Sublime::View* view); /** * @returns if multiple tool views can by created by this factory in the same area. */ virtual bool allowMultiple() const { return false; } }; /** * * Allows to access various parts of the user-interface, like the toolviews or the mainwindow */ class KDEVPLATFORMINTERFACES_EXPORT IUiController { public: virtual ~IUiController(); enum SwitchMode { ThisWindow /**< indicates that the area switch should be in the this window */, NewWindow /**< indicates that the area switch should be using a new window */ }; virtual void switchToArea(const QString &areaName, SwitchMode switchMode) = 0; virtual void addToolView(const QString &name, IToolViewFactory *factory) = 0; virtual void removeToolView(IToolViewFactory *factory) = 0; enum FindFlags { None = 0, Create = 1, ///The tool-view is created if it doesn't exist in the current area yet Raise = 2, ///The tool-view is raised if it was found/created CreateAndRaise = Create | Raise ///The tool view is created and raised }; /** Makes sure that this tool-view exists in the current area, raises it, and returns the contained widget * Returns zero on failure */ virtual QWidget* findToolView(const QString& name, IToolViewFactory *factory, FindFlags flags = CreateAndRaise) = 0; /** * Makes sure that the toolview that contains the widget @p toolViewWidget is visible to the user. */ virtual void raiseToolView(QWidget* toolViewWidget) = 0; /** @return active mainwindow or 0 if no such mainwindow is active.*/ virtual KParts::MainWindow *activeMainWindow() = 0; /*! @p status must implement KDevelop::IStatus */ virtual void registerStatus(QObject* status) = 0; /** * Shows an assistant popup at bottom within the current central widget * @p assistant the assistant that will be shown in a popup */ virtual void popUpAssistant(const QExplicitlySharedDataPointer& assistant) = 0; /** * Hides the assistant if it is currently being shown */ virtual void hideAssistant() = 0; /** * This is meant to be used by IDocument subclasses to initialize the * Sublime::Document. */ virtual Sublime::Controller* controller() = 0; /** Shows an error message in the status bar. * * Unlike all other functions in this class, this function is thread-safe. * You can call it from the background. * * @p message The message * @p timeout The timeout in seconds how long to show the message */ virtual void showErrorMessage(const QString& message, int timeout = 1) = 0; /** @return area for currently active sublime mainwindow or 0 if no sublime mainwindow is active.*/ virtual Sublime::Area *activeArea() = 0; - + + /** + * Widget which is currently responsible for consuming special events in the UI + * (such as shortcuts) + * + * @sa IToolViewActionListener + * @return QWidget implementing the IToolViewActionListener interface + */ + virtual QWidget* activeToolViewActionListener() const = 0; + protected: IUiController(); }; } #endif diff --git a/plugins/grepview/grepoutputview.cpp b/plugins/grepview/grepoutputview.cpp index 84076d4af2..2fba779f11 100644 --- a/plugins/grepview/grepoutputview.cpp +++ b/plugins/grepview/grepoutputview.cpp @@ -1,394 +1,386 @@ /************************************************************************** * Copyright 2010 Silvère Lestang * * Copyright 2010 Julien Desgats * * * * 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 "grepoutputview.h" #include "grepoutputmodel.h" #include "grepoutputdelegate.h" #include "ui_grepoutputview.h" #include "grepviewplugin.h" #include "grepdialog.h" #include "greputil.h" #include "grepjob.h" #include #include #include #include #include #include #include #include #include #include -#include #include using namespace KDevelop; GrepOutputViewFactory::GrepOutputViewFactory(GrepViewPlugin* plugin) : m_plugin(plugin) {} QWidget* GrepOutputViewFactory::create(QWidget* parent) { return new GrepOutputView(parent, m_plugin); } Qt::DockWidgetArea GrepOutputViewFactory::defaultPosition() { return Qt::BottomDockWidgetArea; } QString GrepOutputViewFactory::id() const { return "org.kdevelop.GrepOutputView"; } const int GrepOutputView::HISTORY_SIZE = 5; GrepOutputView::GrepOutputView(QWidget* parent, GrepViewPlugin* plugin) : QWidget(parent) , m_next(0) , m_prev(0) , m_collapseAll(0) , m_expandAll(0) , m_clearSearchHistory(0) , m_statusLabel(0) , m_plugin(plugin) { Ui::GrepOutputView::setupUi(this); setWindowTitle(i18nc("@title:window", "Find/Replace Output View")); setWindowIcon(QIcon::fromTheme("edit-find")); m_prev = new QAction(QIcon::fromTheme("go-previous"), i18n("&Previous Item"), this); m_prev->setEnabled(false); m_next = new QAction(QIcon::fromTheme("go-next"), i18n("&Next Item"), this); m_next->setEnabled(false); m_collapseAll = new QAction(QIcon::fromTheme("arrow-left-double"), i18n("C&ollapse All"), this); // TODO change icon m_collapseAll->setEnabled(false); m_expandAll = new QAction(QIcon::fromTheme("arrow-right-double"), i18n("&Expand All"), this); // TODO change icon m_expandAll->setEnabled(false); QAction *separator = new QAction(this); separator->setSeparator(true); QAction *newSearchAction = new QAction(QIcon::fromTheme("edit-find"), i18n("New &Search"), this); m_clearSearchHistory = new QAction(QIcon::fromTheme("edit-clear-list"), i18n("Clear Search History"), this); addAction(m_prev); addAction(m_next); addAction(m_collapseAll); addAction(m_expandAll); addAction(separator); addAction(newSearchAction); addAction(m_clearSearchHistory); separator = new QAction(this); separator->setSeparator(true); addAction(separator); QWidgetAction *statusWidget = new QWidgetAction(this); m_statusLabel = new QLabel(this); statusWidget->setDefaultWidget(m_statusLabel); addAction(statusWidget); modelSelector->setEditable(false); modelSelector->setContextMenuPolicy(Qt::CustomContextMenu); connect(modelSelector, &KComboBox::customContextMenuRequested, this, &GrepOutputView::modelSelectorContextMenu); connect(modelSelector, static_cast(&KComboBox::currentIndexChanged), this, &GrepOutputView::changeModel); resultsTreeView->setItemDelegate(GrepOutputDelegate::self()); resultsTreeView->setHeaderHidden(true); resultsTreeView->setUniformRowHeights(false); resultsTreeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); connect(m_prev, &QAction::triggered, this, &GrepOutputView::selectPreviousItem); connect(m_next, &QAction::triggered, this, &GrepOutputView::selectNextItem); connect(m_collapseAll, &QAction::triggered, this, &GrepOutputView::collapseAllItems); connect(m_expandAll, &QAction::triggered, this, &GrepOutputView::expandAllItems); connect(applyButton, &QPushButton::clicked, this, &GrepOutputView::onApply); connect(m_clearSearchHistory, &QAction::triggered, this, &GrepOutputView::clearSearchHistory); connect(resultsTreeView, &QTreeView::collapsed, this, &GrepOutputView::updateScrollArea); connect(resultsTreeView, &QTreeView::expanded, this, &GrepOutputView::updateScrollArea); - IPlugin *outputView = ICore::self()->pluginController()->pluginForExtension("org.kdevelop.IOutputView"); - if (qobject_cast(outputView)) { - // can't use new signal/slot syntax here, IOutputView is not a QObject - connect(outputView, SIGNAL(selectPrevItem()), this, SLOT(selectPreviousItem())); - connect(outputView, SIGNAL(selectNextItem()), this, SLOT(selectNextItem())); - } - KConfigGroup cg = ICore::self()->activeSession()->config()->group( "GrepDialog" ); replacementCombo->addItems( cg.readEntry("LastReplacementItems", QStringList()) ); replacementCombo->setInsertPolicy(QComboBox::InsertAtTop); applyButton->setIcon(QIcon::fromTheme("dialog-ok-apply")); connect(replacementCombo, &KComboBox::editTextChanged, this, &GrepOutputView::replacementTextChanged); connect(replacementCombo, static_cast(&KComboBox::returnPressed), this, &GrepOutputView::onApply); connect(newSearchAction, &QAction::triggered, this, &GrepOutputView::showDialog); updateCheckable(); } void GrepOutputView::replacementTextChanged(QString) { updateCheckable(); if (model()) { // see https://bugs.kde.org/show_bug.cgi?id=274902 - renewModel can trigger a call here without an active model updateApplyState(model()->index(0, 0), model()->index(0, 0)); } } GrepOutputView::~GrepOutputView() { KConfigGroup cg = ICore::self()->activeSession()->config()->group( "GrepDialog" ); cg.writeEntry("LastReplacementItems", qCombo2StringList(replacementCombo, true)); emit outputViewIsClosed(); } GrepOutputModel* GrepOutputView::renewModel(QString name, QString descriptionOrUrl) { // Crear oldest model while(modelSelector->count() > GrepOutputView::HISTORY_SIZE) { QVariant var = modelSelector->itemData(GrepOutputView::HISTORY_SIZE - 1); qvariant_cast(var)->deleteLater(); modelSelector->removeItem(GrepOutputView::HISTORY_SIZE - 1); } replacementCombo->clearEditText(); GrepOutputModel* newModel = new GrepOutputModel(resultsTreeView); applyButton->setEnabled(false); // text may be already present newModel->setReplacement(replacementCombo->currentText()); connect(newModel, &GrepOutputModel::rowsRemoved, this, &GrepOutputView::rowsRemoved); connect(resultsTreeView, &QTreeView::activated, newModel, &GrepOutputModel::activate); connect(replacementCombo, &KComboBox::editTextChanged, newModel, &GrepOutputModel::setReplacement); connect(newModel, &GrepOutputModel::rowsInserted, this, &GrepOutputView::expandElements); connect(newModel, &GrepOutputModel::showErrorMessage, this, &GrepOutputView::showErrorMessage); QString prettyUrl = descriptionOrUrl; if(descriptionOrUrl.startsWith('/')) prettyUrl = ICore::self()->projectController()->prettyFileName(QUrl::fromLocalFile(descriptionOrUrl), KDevelop::IProjectController::FormatPlain); // appends new model to history QString displayName = i18n("Search \"%1\" in %2 (at time %3)", name, prettyUrl, QTime::currentTime().toString("hh:mm")); modelSelector->insertItem(0, displayName, qVariantFromValue(newModel)); modelSelector->setCurrentIndex(0);//setCurrentItem(displayName); updateCheckable(); return newModel; } GrepOutputModel* GrepOutputView::model() { return static_cast(resultsTreeView->model()); } void GrepOutputView::changeModel(int index) { if (model()) { disconnect(model(), &GrepOutputModel::showMessage, this, &GrepOutputView::showMessage); disconnect(model(), &GrepOutputModel::dataChanged, this, &GrepOutputView::updateApplyState); } replacementCombo->clearEditText(); //after deleting the whole search history, index is -1 if(index >= 0) { QVariant var = modelSelector->itemData(index); GrepOutputModel *resultModel = static_cast(qvariant_cast(var)); resultsTreeView->setModel(resultModel); connect(model(), &GrepOutputModel::showMessage, this, &GrepOutputView::showMessage); connect(model(), &GrepOutputModel::dataChanged, this, &GrepOutputView::updateApplyState); model()->showMessageEmit(); applyButton->setEnabled(model()->hasResults() && model()->getRootItem() && model()->getRootItem()->checkState() != Qt::Unchecked && !replacementCombo->currentText().isEmpty()); if(model()->hasResults()) expandElements(QModelIndex()); } updateCheckable(); updateApplyState(model()->index(0, 0), model()->index(0, 0)); } void GrepOutputView::setMessage(const QString& msg, MessageType type) { if (type == Error) { QPalette palette = m_statusLabel->palette(); KColorScheme::adjustForeground(palette, KColorScheme::NegativeText, QPalette::WindowText); m_statusLabel->setPalette(palette); } else { m_statusLabel->setPalette(QPalette()); } m_statusLabel->setText(msg); } void GrepOutputView::showErrorMessage( const QString& errorMessage ) { setMessage(errorMessage, Error); } void GrepOutputView::showMessage( KDevelop::IStatus* , const QString& message ) { setMessage(message, Information); } void GrepOutputView::onApply() { if(model()) { Q_ASSERT(model()->rowCount()); // ask a confirmation before an empty string replacement if(replacementCombo->currentText().length() == 0 && KMessageBox::questionYesNo(this, i18n("Do you want to replace with an empty string?"), i18n("Start replacement")) == KMessageBox::No) { return; } setEnabled(false); model()->doReplacements(); setEnabled(true); } } void GrepOutputView::showDialog() { m_plugin->showDialog(true); } void GrepOutputView::expandElements(const QModelIndex&) { m_prev->setEnabled(true); m_next->setEnabled(true); m_collapseAll->setEnabled(true); m_expandAll->setEnabled(true); resultsTreeView->expandAll(); for (int col = 0; col < model()->columnCount(); ++col) resultsTreeView->resizeColumnToContents(col); } void GrepOutputView::selectPreviousItem() { if (!model()) { return; } QModelIndex prev_idx = model()->previousItemIndex(resultsTreeView->currentIndex()); if (prev_idx.isValid()) { resultsTreeView->setCurrentIndex(prev_idx); model()->activate(prev_idx); } } void GrepOutputView::selectNextItem() { if (!model()) { return; } QModelIndex next_idx = model()->nextItemIndex(resultsTreeView->currentIndex()); if (next_idx.isValid()) { resultsTreeView->setCurrentIndex(next_idx); model()->activate(next_idx); } } void GrepOutputView::collapseAllItems() { // Collapse everything resultsTreeView->collapseAll(); // Now reopen the first children, which correspond to the files. resultsTreeView->expand(resultsTreeView->model()->index(0, 0)); } void GrepOutputView::expandAllItems() { resultsTreeView->expandAll(); } void GrepOutputView::rowsRemoved() { m_prev->setEnabled(model()->rowCount()); m_next->setEnabled(model()->rowCount()); } void GrepOutputView::updateApplyState(const QModelIndex& topLeft, const QModelIndex& bottomRight) { Q_UNUSED(bottomRight); if (!model() || !model()->hasResults()) { applyButton->setEnabled(false); return; } // we only care about the root item if(!topLeft.parent().isValid()) { applyButton->setEnabled(topLeft.data(Qt::CheckStateRole) != Qt::Unchecked && model()->itemsCheckable()); } } void GrepOutputView::updateCheckable() { if(model()) model()->makeItemsCheckable(!replacementCombo->currentText().isEmpty() || model()->itemsCheckable()); } void GrepOutputView::clearSearchHistory() { GrepJob *runningJob = m_plugin->grepJob(); if(runningJob) { runningJob->kill(); } while(modelSelector->count() > 0) { QVariant var = modelSelector->itemData(0); qvariant_cast(var)->deleteLater(); modelSelector->removeItem(0); } applyButton->setEnabled(false); m_statusLabel->setText(QString()); } void GrepOutputView::modelSelectorContextMenu(const QPoint& pos) { QPoint globalPos = modelSelector->mapToGlobal(pos); QMenu myMenu; myMenu.addAction(m_clearSearchHistory); myMenu.exec(globalPos); } void GrepOutputView::updateScrollArea(const QModelIndex& index) { resultsTreeView->resizeColumnToContents( index.column() ); } diff --git a/plugins/grepview/grepoutputview.h b/plugins/grepview/grepoutputview.h index 881dbc0a94..b63445b8ff 100644 --- a/plugins/grepview/grepoutputview.h +++ b/plugins/grepview/grepoutputview.h @@ -1,99 +1,101 @@ /************************************************************************** * Copyright 2010 Silvère Lestang * * Copyright 2010 Julien Desgats * * * * 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. * * * ***************************************************************************/ #ifndef KDEVPLATFORM_PLUGIN_GREPOUTPUTVIEW_H #define KDEVPLATFORM_PLUGIN_GREPOUTPUTVIEW_H #include +#include #include "ui_grepoutputview.h" namespace KDevelop { class IStatus; } class QModelIndex; class GrepViewPlugin; class GrepOutputModel; class GrepOutputDelegate; class GrepOutputViewFactory: public KDevelop::IToolViewFactory { public: GrepOutputViewFactory(GrepViewPlugin* plugin); virtual QWidget* create(QWidget* parent = 0); virtual Qt::DockWidgetArea defaultPosition(); virtual QString id() const; private: GrepViewPlugin* m_plugin; }; -class GrepOutputView : public QWidget, Ui::GrepOutputView +class GrepOutputView : public QWidget, Ui::GrepOutputView, public KDevelop::IToolViewActionListener { - Q_OBJECT + Q_OBJECT + Q_INTERFACES(KDevelop::IToolViewActionListener) public: enum MessageType { Information, Error }; GrepOutputView(QWidget* parent, GrepViewPlugin* plugin); ~GrepOutputView(); GrepOutputModel* model(); /** * This causes the creation of a new model, the old one is kept in model history. * Oldest models are deleted if needed. * @return pointer to the new model */ GrepOutputModel* renewModel(QString name, QString descriptionOrUrl); void setMessage(const QString& msg, MessageType type = Information); public Q_SLOTS: void showErrorMessage( const QString& errorMessage ); void showMessage( KDevelop::IStatus*, const QString& message ); void updateApplyState(const QModelIndex &topLeft, const QModelIndex &bottomRight); void changeModel(int index); void replacementTextChanged(QString); Q_SIGNALS: void outputViewIsClosed(); private: static const int HISTORY_SIZE; QAction* m_next; QAction* m_prev; QAction* m_collapseAll; QAction* m_expandAll; QAction* m_clearSearchHistory; QLabel* m_statusLabel; GrepViewPlugin *m_plugin; private slots: - void selectPreviousItem(); - void selectNextItem(); + void selectPreviousItem() override; + void selectNextItem() override; void collapseAllItems(); void expandAllItems(); void onApply(); void showDialog(); void expandElements( const QModelIndex & parent ); void rowsRemoved(); void clearSearchHistory(); void modelSelectorContextMenu(const QPoint& pos); void updateScrollArea( const QModelIndex &index ); void updateCheckable(); }; #endif // KDEVPLATFORM_PLUGIN_GREPOUTPUTVIEW_H diff --git a/plugins/standardoutputview/outputwidget.cpp b/plugins/standardoutputview/outputwidget.cpp index 5babb1977a..e3256c41ac 100644 --- a/plugins/standardoutputview/outputwidget.cpp +++ b/plugins/standardoutputview/outputwidget.cpp @@ -1,708 +1,705 @@ /* This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * Copyright 2007 Dukju Ahn * * 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 "outputwidget.h" #include "standardoutputview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "toolviewdata.h" #include "debug.h" Q_LOGGING_CATEGORY(PLUGIN_STANDARDOUTPUTVIEW, "kdevplatform.plugins.standardoutputview") Q_DECLARE_METATYPE(QTreeView*) OutputWidget::OutputWidget(QWidget* parent, const ToolViewData* tvdata) : QWidget( parent ) , tabwidget(0) , stackwidget(0) , data(tvdata) , m_closeButton(0) , m_closeOthersAction(0) , nextAction(0) , previousAction(0) , activateOnSelect(0) , focusOnSelect(0) , filterInput(0) , filterAction(0) { setWindowTitle(i18n("Output View")); setWindowIcon(tvdata->icon); QVBoxLayout* layout = new QVBoxLayout(this); layout->setMargin(0); if( data->type & KDevelop::IOutputView::MultipleView ) { tabwidget = new QTabWidget(this); layout->addWidget( tabwidget ); m_closeButton = new QToolButton( this ); connect( m_closeButton, &QToolButton::clicked, this, &OutputWidget::closeActiveView ); m_closeButton->setIcon( QIcon::fromTheme("tab-close") ); m_closeButton->setToolTip( i18n( "Close the currently active output view") ); m_closeOthersAction = new QAction( this ); connect(m_closeOthersAction, &QAction::triggered, this, &OutputWidget::closeOtherViews); m_closeOthersAction->setIcon(QIcon::fromTheme("tab-close-other")); m_closeOthersAction->setToolTip( i18n( "Close all other output views" ) ); m_closeOthersAction->setText( m_closeOthersAction->toolTip() ); addAction(m_closeOthersAction); tabwidget->setCornerWidget(m_closeButton, Qt::TopRightCorner); } else if ( data->type == KDevelop::IOutputView::HistoryView ) { stackwidget = new QStackedWidget( this ); layout->addWidget( stackwidget ); previousAction = new QAction( QIcon::fromTheme( "go-previous" ), i18n("Previous"), this ); connect(previousAction, &QAction::triggered, this, &OutputWidget::previousOutput); addAction(previousAction); nextAction = new QAction( QIcon::fromTheme( "go-next" ), i18n("Next"), this ); connect(nextAction, &QAction::triggered, this, &OutputWidget::nextOutput); addAction(nextAction); } addAction(dynamic_cast(data->plugin->actionCollection()->action("prev_error"))); addAction(dynamic_cast(data->plugin->actionCollection()->action("next_error"))); activateOnSelect = new KToggleAction( QIcon(), i18n("Select activated Item"), this ); activateOnSelect->setChecked( true ); focusOnSelect = new KToggleAction( QIcon(), i18n("Focus when selecting Item"), this ); focusOnSelect->setChecked( false ); if( data->option & KDevelop::IOutputView::ShowItemsButton ) { addAction(activateOnSelect); addAction(focusOnSelect); } QAction *separator = new QAction(this); separator->setSeparator(true); QAction* selectAllAction = KStandardAction::selectAll(this, SLOT(selectAll()), this); selectAllAction->setShortcut(QKeySequence()); //FIXME: why does CTRL-A conflict with Katepart (while CTRL-Cbelow doesn't) ? selectAllAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); addAction(selectAllAction); QAction* copyAction = KStandardAction::copy(this, SLOT(copySelection()), this); copyAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); addAction(copyAction); if( data->option & KDevelop::IOutputView::AddFilterAction ) { addAction(separator); filterInput = new QLineEdit(); filterInput->setMaximumWidth(150); filterInput->setMinimumWidth(100); filterInput->setPlaceholderText(i18n("Search...")); filterInput->setClearButtonEnabled(true); filterInput->setToolTip(i18n("Enter a wild card string to filter the output view")); filterAction = new QWidgetAction(this); filterAction->setDefaultWidget(filterInput); addAction(filterAction); connect(filterInput, &QLineEdit::textEdited, this, &OutputWidget::outputFilter ); if( data->type & KDevelop::IOutputView::MultipleView ) { connect(tabwidget, &QTabWidget::currentChanged, this, &OutputWidget::updateFilter); } else if ( data->type == KDevelop::IOutputView::HistoryView ) { connect(stackwidget, &QStackedWidget::currentChanged, this, &OutputWidget::updateFilter); } } addActions(data->actionList); connect( data, &ToolViewData::outputAdded, this, &OutputWidget::addOutput ); connect( this, &OutputWidget::outputRemoved, data->plugin, &StandardOutputView::outputRemoved ); - connect( data->plugin, &StandardOutputView::selectNextItem, this, &OutputWidget::selectNextItem ); - connect( data->plugin, &StandardOutputView::selectPrevItem, this, &OutputWidget::selectPrevItem ); - foreach( int id, data->outputdata.keys() ) { changeModel( id ); changeDelegate( id ); } enableActions(); } void OutputWidget::addOutput( int id ) { QTreeView* listview = createListView(id); setCurrentWidget( listview ); connect( data->outputdata.value(id), &OutputData::modelChanged, this, &OutputWidget::changeModel); connect( data->outputdata.value(id), &OutputData::delegateChanged, this, &OutputWidget::changeDelegate); enableActions(); } void OutputWidget::setCurrentWidget( QTreeView* view ) { if( data->type & KDevelop::IOutputView::MultipleView ) { tabwidget->setCurrentWidget( view ); } else if( data->type & KDevelop::IOutputView::HistoryView ) { stackwidget->setCurrentWidget( view ); } } void OutputWidget::changeDelegate( int id ) { if( data->outputdata.contains( id ) && views.contains( id ) ) { views.value(id)->setItemDelegate(data->outputdata.value(id)->delegate); } else { addOutput(id); } } void OutputWidget::changeModel( int id ) { if( data->outputdata.contains( id ) && views.contains( id ) ) { OutputData* od = data->outputdata.value(id); views.value( id )->setModel(od->model); if (!od->model) return; disconnect( od->model,&QAbstractItemModel::rowsInserted, this, &OutputWidget::rowsInserted ); if( od->behaviour & KDevelop::IOutputView::AutoScroll ) { connect( od->model,&QAbstractItemModel::rowsInserted, this, &OutputWidget::rowsInserted ); } } else { addOutput( id ); } } void OutputWidget::removeOutput( int id ) { if( data->outputdata.contains( id ) && views.contains( id ) ) { QTreeView* view = views.value(id); if( data->type & KDevelop::IOutputView::MultipleView || data->type & KDevelop::IOutputView::HistoryView ) { if( data->type & KDevelop::IOutputView::MultipleView ) { int idx = tabwidget->indexOf( view ); if( idx != -1 ) { tabwidget->removeTab( idx ); if( proxyModels.contains( idx ) ) { delete proxyModels.take( idx ); filters.remove( idx ); } } } else { int idx = stackwidget->indexOf( view ); if( idx != -1 && proxyModels.contains( idx ) ) { delete proxyModels.take( idx ); filters.remove( idx ); } stackwidget->removeWidget( view ); } delete view; } else { views.value( id )->setModel( 0 ); views.value( id )->setItemDelegate( 0 ); if( proxyModels.contains( 0 ) ) { delete proxyModels.take( 0 ); filters.remove( 0 ); } } disconnect( data->outputdata.value( id )->model,&QAbstractItemModel::rowsInserted, this, &OutputWidget::rowsInserted ); views.remove( id ); m_scrollDelay.remove( view ); emit outputRemoved( data->toolViewId, id ); } enableActions(); } void OutputWidget::closeActiveView() { QWidget* widget = tabwidget->currentWidget(); if( !widget ) return; foreach( int id, views.keys() ) { if( views.value(id) == widget ) { OutputData* od = data->outputdata.value(id); if( od->behaviour & KDevelop::IOutputView::AllowUserClose ) { data->plugin->removeOutput( id ); } } } enableActions(); } void OutputWidget::closeOtherViews() { QWidget* widget = tabwidget->currentWidget(); if (!widget) return; foreach (int id, views.keys()) { if (views.value(id) == widget) { continue; // leave the active view open } OutputData* od = data->outputdata.value(id); if (od->behaviour & KDevelop::IOutputView::AllowUserClose) { data->plugin->removeOutput( id ); } } enableActions(); } QWidget* OutputWidget::currentWidget() const { QWidget* widget; if( data->type & KDevelop::IOutputView::MultipleView ) { widget = tabwidget->currentWidget(); } else if( data->type & KDevelop::IOutputView::HistoryView ) { widget = stackwidget->currentWidget(); } else { widget = views.begin().value(); } return widget; } KDevelop::IOutputViewModel *OutputWidget::outputViewModel() const { QWidget* widget = currentWidget(); if( !widget || !widget->isVisible() ) return nullptr; auto view = qobject_cast(widget); if( !view ) return nullptr; QAbstractItemModel *absmodel = view->model(); KDevelop::IOutputViewModel *iface = dynamic_cast(absmodel); if ( ! iface ) { // try if it's a proxy model? if ( QAbstractProxyModel* proxy = qobject_cast(absmodel) ) { iface = dynamic_cast(proxy->sourceModel()); } } return iface; } void OutputWidget::eventuallyDoFocus() { QWidget* widget = currentWidget(); if( focusOnSelect->isChecked() && !widget->hasFocus() ) { widget->setFocus( Qt::OtherFocusReason ); } } QAbstractItemView *OutputWidget::outputView() const { auto widget = currentWidget(); return qobject_cast(widget); } void OutputWidget::activateIndex(const QModelIndex &index, QAbstractItemView *view, KDevelop::IOutputViewModel *iface) { if( ! index.isValid() ) return; int tabIndex = currentOutputIndex(); QModelIndex sourceIndex = index; QModelIndex viewIndex = index; if( QAbstractProxyModel* proxy = proxyModels.value(tabIndex) ) { if ( index.model() == proxy ) { // index is from the proxy, map it to the source sourceIndex = proxy->mapToSource(index); } else if (proxy == view->model()) { // index is from the source, map it to the proxy viewIndex = proxy->mapFromSource(index); } } view->setCurrentIndex( viewIndex ); view->scrollTo( viewIndex ); if( activateOnSelect->isChecked() ) { iface->activate( sourceIndex ); } } void OutputWidget::selectNextItem() { selectItem(Next); } -void OutputWidget::selectPrevItem() +void OutputWidget::selectPreviousItem() { selectItem(Previous); } void OutputWidget::selectItem(Direction direction) { auto view = outputView(); auto iface = outputViewModel(); if ( ! view || ! iface ) return; eventuallyDoFocus(); auto index = view->currentIndex(); if (QAbstractProxyModel* proxy = proxyModels.value(currentOutputIndex())) { if ( index.model() == proxy ) { // index is from the proxy, map it to the source index = proxy->mapToSource(index); } } const auto newIndex = direction == Previous ? iface->previousHighlightIndex( index ) : iface->nextHighlightIndex( index ); qCDebug(PLUGIN_STANDARDOUTPUTVIEW) << "old:" << index << "- new:" << newIndex; activateIndex(newIndex, view, iface); } void OutputWidget::activate(const QModelIndex& index) { auto iface = outputViewModel(); auto view = outputView(); if( ! view || ! iface ) return; activateIndex(index, view, iface); } static QTreeView* createFocusedTreeView( QWidget* parent ) { QTreeView* listview = new KDevelop::FocusedTreeView(parent); listview->setEditTriggers( QAbstractItemView::NoEditTriggers ); listview->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); //Always enable the scrollbar, so it doesn't flash around listview->setHeaderHidden(true); listview->setUniformRowHeights(true); listview->setRootIsDecorated(false); listview->setSelectionMode( QAbstractItemView::ContiguousSelection ); return listview; } QTreeView* OutputWidget::createListView(int id) { QTreeView* listview = 0; bool newView = true; if( !views.contains(id) ) { if( data->type & KDevelop::IOutputView::MultipleView || data->type & KDevelop::IOutputView::HistoryView ) { qCDebug(PLUGIN_STANDARDOUTPUTVIEW) << "creating listview"; listview = createFocusedTreeView(this); views[id] = listview; connect( listview, &QTreeView::activated, this, &OutputWidget::activate); connect( listview, &QTreeView::clicked, this, &OutputWidget::activate); if( data->type & KDevelop::IOutputView::MultipleView ) { tabwidget->addTab( listview, data->outputdata.value(id)->title ); } else { stackwidget->addWidget( listview ); stackwidget->setCurrentWidget( listview ); } } else { if( views.isEmpty() ) { listview = createFocusedTreeView(this); layout()->addWidget( listview ); connect( listview, &QTreeView::activated, this, &OutputWidget::activate); connect( listview, &QTreeView::clicked, this, &OutputWidget::activate); } else { listview = views.begin().value(); newView = false; } views[id] = listview; } if (newView) { auto timer = new QTimer(listview); timer->setSingleShot(true); timer->setInterval(300); timer->setProperty("view", QVariant::fromValue(listview)); m_scrollDelay[listview] = {timer, -1, -1}; connect(timer, &QTimer::timeout, this, static_cast(&OutputWidget::delayedScroll)); } changeModel( id ); changeDelegate( id ); } else { listview = views.value(id); } enableActions(); return listview; } void OutputWidget::raiseOutput(int id) { if( views.contains(id) ) { if( data->type & KDevelop::IOutputView::MultipleView ) { int idx = tabwidget->indexOf( views.value(id) ); if( idx >= 0 ) { tabwidget->setCurrentIndex( idx ); } } else if( data->type & KDevelop::IOutputView::HistoryView ) { int idx = stackwidget->indexOf( views.value(id) ); if( idx >= 0 ) { stackwidget->setCurrentIndex( idx ); } } } enableActions(); } void OutputWidget::nextOutput() { if( stackwidget && stackwidget->currentIndex() < stackwidget->count()-1 ) { stackwidget->setCurrentIndex( stackwidget->currentIndex()+1 ); } enableActions(); } void OutputWidget::previousOutput() { if( stackwidget && stackwidget->currentIndex() > 0 ) { stackwidget->setCurrentIndex( stackwidget->currentIndex()-1 ); } enableActions(); } void OutputWidget::enableActions() { if( data->type == KDevelop::IOutputView::HistoryView ) { Q_ASSERT(stackwidget); Q_ASSERT(nextAction); Q_ASSERT(previousAction); previousAction->setEnabled( ( stackwidget->currentIndex() > 0 ) ); nextAction->setEnabled( ( stackwidget->currentIndex() < stackwidget->count() - 1 ) ); } } void OutputWidget::rowsInserted(const QModelIndex& parent, int from, int to) { if (parent.isValid()) { return; } auto model = qobject_cast(sender()); Q_ASSERT(model); foreach (QTreeView* view, views) { if (view->model() == model) { auto& data = m_scrollDelay[view]; if (data.from == -1) { data.from = from; } data.to = to; if (!data.timer->isActive()) { data.timer->start(); } } } } void OutputWidget::delayedScroll() { auto timer = qobject_cast(sender()); Q_ASSERT(timer); auto view = timer->property("view").value(); Q_ASSERT(view); delayedScroll(view); } void OutputWidget::delayedScroll(QTreeView* view) { Q_ASSERT(m_scrollDelay.contains(view)); auto& data = m_scrollDelay[view]; QModelIndex pre = view->model()->index(data.from - 1, 0); bool scroll = !pre.isValid(); if (!scroll && data.to == view->model()->rowCount() - 1) { auto rect = view->visualRect(pre); scroll = rect.isValid() && view->viewport()->rect().intersects(rect); } if (scroll) { view->scrollToBottom(); } data.from = -1; } void OutputWidget::scrollToIndex( const QModelIndex& idx ) { QWidget* w = currentWidget(); if( !w ) return; QAbstractItemView *view = dynamic_cast(w); view->scrollTo( idx ); } void OutputWidget::copySelection() { QWidget* widget = currentWidget(); if( !widget ) return; QAbstractItemView *view = dynamic_cast(widget); if( !view ) return; QClipboard *cb = QApplication::clipboard(); QModelIndexList indexes = view->selectionModel()->selectedRows(); QString content; Q_FOREACH( const QModelIndex& index, indexes) { content += index.data().toString() + '\n'; } cb->setText(content); } void OutputWidget::selectAll() { QWidget* widget = currentWidget(); if( !widget ) return; QAbstractItemView *view = dynamic_cast(widget); if( !view ) return; view->selectAll(); } int OutputWidget::currentOutputIndex() { int index = 0; if( data->type & KDevelop::IOutputView::MultipleView ) { index = tabwidget->currentIndex(); } else if( data->type & KDevelop::IOutputView::HistoryView ) { index = stackwidget->currentIndex(); } return index; } void OutputWidget::outputFilter(const QString& filter) { QWidget* widget = currentWidget(); if( !widget ) return; QAbstractItemView *view = dynamic_cast(widget); if( !view ) return; int index = currentOutputIndex(); auto proxyModel = dynamic_cast(view->model()); if( !proxyModel ) { proxyModel = new QSortFilterProxyModel(view->model()); proxyModel->setDynamicSortFilter(true); proxyModel->setSourceModel(view->model()); proxyModels.insert(index, proxyModel); view->setModel(proxyModel); } QRegExp regExp(filter, Qt::CaseInsensitive); proxyModel->setFilterRegExp(regExp); filters[index] = filter; } void OutputWidget::updateFilter(int index) { if(filters.contains(index)) { filterInput->setText(filters[index]); } else { filterInput->clear(); } } void OutputWidget::setTitle(int outputId, const QString& title) { if( data->type & KDevelop::IOutputView::MultipleView ) { tabwidget->setTabText(outputId - 1, title); } } diff --git a/plugins/standardoutputview/outputwidget.h b/plugins/standardoutputview/outputwidget.h index 88415cfd47..54c0f7292d 100644 --- a/plugins/standardoutputview/outputwidget.h +++ b/plugins/standardoutputview/outputwidget.h @@ -1,126 +1,129 @@ /* This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * Copyright 2007 Dukju Ahn * * 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. */ #ifndef KDEVPLATFORM_PLUGIN_OUTPUTWIDGET_H #define KDEVPLATFORM_PLUGIN_OUTPUTWIDGET_H #include #include + +#include #include #include class QAbstractProxyModel; class QAbstractItemView; class QString; class StandardOutputView; class QSignalMapper; class QStackedWidget; class QTreeView; class QToolButton; class QWidgetAction; class QSortFilterProxyModel; class QModelIndex; class ToolViewData; class QTabWidget; class KToggleAction; class QAction; class QAction; class QLineEdit; class StandardOutputViewTest; -class OutputWidget : public QWidget +class OutputWidget : public QWidget, public KDevelop::IToolViewActionListener { -Q_OBJECT + Q_OBJECT + Q_INTERFACES(KDevelop::IToolViewActionListener) friend class StandardOutputViewTest; public: OutputWidget(QWidget* parent, const ToolViewData* data); void removeOutput( int id ); void raiseOutput( int id ); public Q_SLOTS: void addOutput( int id ); void changeModel( int id ); void changeDelegate( int id ); void closeActiveView(); void closeOtherViews(); - void selectNextItem(); - void selectPrevItem(); + void selectNextItem() override; + void selectPreviousItem() override; void activate(const QModelIndex&); void scrollToIndex( const QModelIndex& ); void setTitle(int outputId, const QString& title); Q_SIGNALS: void outputRemoved( int, int ); private slots: void nextOutput(); void previousOutput(); void rowsInserted(const QModelIndex&, int, int); void copySelection(); void selectAll(); void outputFilter(const QString& filter); void updateFilter(int index); void delayedScroll(); private: enum Direction { Next, Previous }; void selectItem(Direction direction); QTreeView* createListView(int id); void setCurrentWidget( QTreeView* view ); QWidget* currentWidget() const; void enableActions(); KDevelop::IOutputViewModel* outputViewModel() const; QAbstractItemView* outputView() const; void activateIndex(const QModelIndex& index, QAbstractItemView* view, KDevelop::IOutputViewModel* iface); void eventuallyDoFocus(); int currentOutputIndex(); void delayedScroll(QTreeView* view); QMap views; struct DelayData { QTimer* timer; int from; int to; }; QMap m_scrollDelay; QMap proxyModels; QMap filters; QTabWidget* tabwidget; QStackedWidget* stackwidget; const ToolViewData* data; QToolButton* m_closeButton; QAction* m_closeOthersAction; QAction* nextAction; QAction* previousAction; KToggleAction* activateOnSelect; KToggleAction* focusOnSelect; QLineEdit *filterInput; QWidgetAction* filterAction; }; #endif diff --git a/plugins/standardoutputview/standardoutputview.cpp b/plugins/standardoutputview/standardoutputview.cpp index 414fc87526..451bc06535 100644 --- a/plugins/standardoutputview/standardoutputview.cpp +++ b/plugins/standardoutputview/standardoutputview.cpp @@ -1,331 +1,317 @@ /* KDevelop Standard OutputView * * Copyright 2006-2007 Andreas Pakulat * Copyright 2007 Dukju Ahn * * 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 "standardoutputview.h" #include "outputwidget.h" #include "toolviewdata.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include class OutputViewFactory : public KDevelop::IToolViewFactory{ public: OutputViewFactory(const ToolViewData* data): m_data(data) {} virtual QWidget* create(QWidget *parent = 0) { return new OutputWidget( parent, m_data ); } virtual Qt::DockWidgetArea defaultPosition() { return Qt::BottomDockWidgetArea; } virtual void viewCreated( Sublime::View* view ) { m_data->views << view; } virtual QString id() const { //NOTE: id must be unique, see e.g. https://bugs.kde.org/show_bug.cgi?id=287093 return "org.kdevelop.OutputView." + QString::number(m_data->toolViewId); } private: const ToolViewData *m_data; }; StandardOutputView::StandardOutputView(QObject *parent, const QVariantList &) : KDevelop::IPlugin("kdevstandardoutputview", parent) { KDEV_USE_EXTENSION_INTERFACE( KDevelop::IOutputView ) setXMLFile("kdevstandardoutputview.rc"); - // setup actions - QAction* action; - - action = actionCollection()->addAction("next_error"); - action->setText(i18n("Jump to Next Outputmark")); - actionCollection()->setDefaultShortcut( action, QKeySequence(Qt::Key_F4) ); - action->setIcon(QIcon::fromTheme("arrow-right")); - connect(action, &QAction::triggered, this, &StandardOutputView::selectNextItem); - - action = actionCollection()->addAction("prev_error"); - action->setText(i18n("Jump to Previous Outputmark")); - actionCollection()->setDefaultShortcut( action, QKeySequence(Qt::SHIFT | Qt::Key_F4) ); - action->setIcon(QIcon::fromTheme("arrow-left")); - connect(action, &QAction::triggered, this, &StandardOutputView::selectPrevItem); connect(KDevelop::ICore::self()->uiController()->controller(), &Sublime::Controller::aboutToRemoveView, this, &StandardOutputView::removeSublimeView); } void StandardOutputView::removeSublimeView( Sublime::View* v ) { foreach( ToolViewData* d, toolviews ) { if( d->views.contains(v) ) { if( d->views.count() == 1 ) { toolviews.remove( d->toolViewId ); ids.removeAll( d->toolViewId ); delete d; } else { d->views.removeAll(v); } } } } StandardOutputView::~StandardOutputView() { } int StandardOutputView::standardToolView( KDevelop::IOutputView::StandardToolView view ) { if( standardViews.contains( view ) ) { return standardViews.value( view ); } int ret = -1; switch( view ) { case KDevelop::IOutputView::BuildView: { ret = registerToolView( i18nc("@title:window", "Build"), KDevelop::IOutputView::HistoryView, QIcon::fromTheme("run-build"), KDevelop::IOutputView::AddFilterAction ); break; } case KDevelop::IOutputView::RunView: { ret = registerToolView( i18nc("@title:window", "Run"), KDevelop::IOutputView::MultipleView, QIcon::fromTheme("system-run"), KDevelop::IOutputView::AddFilterAction ); break; } case KDevelop::IOutputView::DebugView: { ret = registerToolView( i18nc("@title:window", "Debug"), KDevelop::IOutputView::MultipleView, QIcon::fromTheme("debugger"), KDevelop::IOutputView::AddFilterAction ); break; } case KDevelop::IOutputView::TestView: { ret = registerToolView( i18nc("@title:window", "Test"), KDevelop::IOutputView::HistoryView, QIcon::fromTheme("system-run")); break; } case KDevelop::IOutputView::VcsView: { ret = registerToolView( i18nc("@title:window", "Version Control"), KDevelop::IOutputView::HistoryView, QIcon::fromTheme("system-run")); break; } } Q_ASSERT(ret != -1); standardViews[view] = ret; return ret; } int StandardOutputView::registerToolView( const QString& title, KDevelop::IOutputView::ViewType type, const QIcon& icon, Options option, const QList& actionList ) { // try to reuse existing toolview foreach( ToolViewData* d, toolviews ) { if ( d->type == type && d->title == title ) { return d->toolViewId; } } // register new tool view const int newid = ids.isEmpty() ? 0 : (ids.last() + 1); qCDebug(PLUGIN_STANDARDOUTPUTVIEW) << "Registering view" << title << "with type:" << type << "id:" << newid; ToolViewData* tvdata = new ToolViewData( this ); tvdata->toolViewId = newid; tvdata->type = type; tvdata->title = title; tvdata->icon = icon; tvdata->plugin = this; tvdata->option = option; tvdata->actionList = actionList; core()->uiController()->addToolView( title, new OutputViewFactory( tvdata ) ); ids << newid; toolviews[newid] = tvdata; return newid; } int StandardOutputView::registerOutputInToolView( int toolViewId, const QString& title, KDevelop::IOutputView::Behaviours behaviour ) { if( !toolviews.contains( toolViewId ) ) return -1; int newid; if( ids.isEmpty() ) { newid = 0; } else { newid = ids.last()+1; } ids << newid; toolviews.value( toolViewId )->addOutput( newid, title, behaviour ); return newid; } void StandardOutputView::raiseOutput(int outputId) { foreach( int _id, toolviews.keys() ) { if( toolviews.value( _id )->outputdata.contains( outputId ) ) { foreach( Sublime::View* v, toolviews.value( _id )->views ) { if( v->hasWidget() ) { OutputWidget* w = qobject_cast( v->widget() ); w->raiseOutput( outputId ); v->requestRaise(); } } } } } void StandardOutputView::setModel( int outputId, QAbstractItemModel* model ) { int tvid = -1; foreach( int _id, toolviews.keys() ) { if( toolviews.value( _id )->outputdata.contains( outputId ) ) { tvid = _id; break; } } if( tvid == -1 ) qCDebug(PLUGIN_STANDARDOUTPUTVIEW) << "Trying to set model on unknown view-id:" << outputId; else { toolviews.value( tvid )->outputdata.value( outputId )->setModel( model ); } } void StandardOutputView::setDelegate( int outputId, QAbstractItemDelegate* delegate ) { int tvid = -1; foreach( int _id, toolviews.keys() ) { if( toolviews.value( _id )->outputdata.contains( outputId ) ) { tvid = _id; break; } } if( tvid == -1 ) qCDebug(PLUGIN_STANDARDOUTPUTVIEW) << "Trying to set model on unknown view-id:" << outputId; else { toolviews.value( tvid )->outputdata.value( outputId )->setDelegate( delegate ); } } void StandardOutputView::removeToolView( int toolviewId ) { if( toolviews.contains(toolviewId) ) { ToolViewData* td = toolviews.value(toolviewId); foreach( Sublime::View* view, td->views ) { if( view->hasWidget() ) { OutputWidget* outputWidget = qobject_cast( view->widget() ); foreach( int outid, td->outputdata.keys() ) { outputWidget->removeOutput( outid ); } } foreach( Sublime::Area* area, KDevelop::ICore::self()->uiController()->controller()->allAreas() ) { area->removeToolView( view ); } } delete td; toolviews.remove(toolviewId); emit toolViewRemoved(toolviewId); } } OutputWidget* StandardOutputView::outputWidgetForId( int outputId ) const { foreach( ToolViewData* td, toolviews ) { if( td->outputdata.contains( outputId ) ) { foreach( Sublime::View* view, td->views ) { if( view->hasWidget() ) return qobject_cast( view->widget() ); } } } return 0; } void StandardOutputView::scrollOutputTo( int outputId, const QModelIndex& idx ) { OutputWidget* widget = outputWidgetForId( outputId ); if( widget ) widget->scrollToIndex( idx ); } void StandardOutputView::removeOutput( int outputId ) { foreach( ToolViewData* td, toolviews ) { if( td->outputdata.contains( outputId ) ) { foreach( Sublime::View* view, td->views ) { if( view->hasWidget() ) qobject_cast( view->widget() )->removeOutput( outputId ); } td->outputdata.remove( outputId ); } } } void StandardOutputView::setTitle(int outputId, const QString& title) { outputWidgetForId(outputId)->setTitle(outputId, title); } diff --git a/plugins/standardoutputview/standardoutputview.h b/plugins/standardoutputview/standardoutputview.h index 772f75ad9d..28a51c8e90 100644 --- a/plugins/standardoutputview/standardoutputview.h +++ b/plugins/standardoutputview/standardoutputview.h @@ -1,96 +1,94 @@ /* KDevelop Standard OutputView * * Copyright 2006-2007 Andreas Pakulat * Copyright 2007 Dukju Ahn * * 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. */ #ifndef KDEVPLATFORM_PLUGIN_STANDARDOUTPUTVIEW_H #define KDEVPLATFORM_PLUGIN_STANDARDOUTPUTVIEW_H #include #include #include template class QList; class QAbstractItemModel; class QString; class QModelIndex; class QAbstractItemDelegate; class OutputWidget; class ToolViewData; /** @author Andreas Pakulat */ namespace Sublime { class View; } class StandardOutputView : public KDevelop::IPlugin, public KDevelop::IOutputView { Q_OBJECT Q_INTERFACES( KDevelop::IOutputView ) public: explicit StandardOutputView(QObject *parent = 0, const QVariantList &args = QVariantList()); virtual ~StandardOutputView(); int standardToolView( KDevelop::IOutputView::StandardToolView view ); int registerToolView( const QString& title, KDevelop::IOutputView::ViewType type = KDevelop::IOutputView::OneView, const QIcon& icon = QIcon(), KDevelop::IOutputView::Options option = ShowItemsButton, const QList& actionList = QList()); int registerOutputInToolView( int toolviewId, const QString& title, KDevelop::IOutputView::Behaviours behaviour = KDevelop::IOutputView::AllowUserClose ); void raiseOutput( int id ); void setModel( int outputId, QAbstractItemModel* model ); void setDelegate( int outputId, QAbstractItemDelegate* delegate ); OutputWidget* outputWidgetForId( int outputId ) const; virtual void removeToolView( int toolviewId ); virtual void removeOutput( int outputId ); virtual void scrollOutputTo( int outputId, const QModelIndex& idx ); virtual void setTitle(int outputId, const QString& title); public Q_SLOTS: void removeSublimeView( Sublime::View* ); Q_SIGNALS: void activated( const QModelIndex& ); - void selectNextItem(); - void selectPrevItem(); void outputRemoved( int toolviewId, int outputId ); void toolViewRemoved( int toolviewId ); private: QMap toolviews; QList ids; QMap standardViews; }; #endif // KDEVPLATFORM_PLUGIN_STANDARDOUTPUTVIEW_H diff --git a/shell/mainwindow_actions.cpp b/shell/mainwindow_actions.cpp index ae670df249..8196afa85f 100644 --- a/shell/mainwindow_actions.cpp +++ b/shell/mainwindow_actions.cpp @@ -1,232 +1,252 @@ /* This file is part of the KDevelop project Copyright 2002 Falk Brettschneider Copyright 2003 John Firebaugh Copyright 2006 Adam Treat Copyright 2006, 2007 Alexander Dymo 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 #include #include #include #include #include #include #include "core.h" #include "documentcontroller.h" #include "mainwindow_p.h" #include "sessiondialog.h" #include "uicontroller.h" #include "mainwindow.h" #include "loadedpluginsdialog.h" + +#include #include namespace KDevelop { // merge the gotoNext and gotoPrev code, to prevent copy/paste errors static void gotoPrevNextWindow(bool next) { UiController* ui = Core::self()->uiControllerInternal(); if( !ui->activeSublimeWindow() ) return; Sublime::Area* activeArea = ui->activeArea(); if (!activeArea) return; Sublime::View* activeView = ui->activeSublimeWindow()->activeView(); Sublime::AreaIndex* index = activeArea->indexOf(activeView); if (!index) return; int viewIndex = index->views().indexOf(activeView); viewIndex = next ? viewIndex + 1 : viewIndex -1; if (viewIndex < 0) viewIndex = index->views().count() - 1; else if (viewIndex >= index->views().count()) viewIndex = 0; if (viewIndex >= 0 && viewIndex < index->views().count()) ui->activeSublimeWindow()->activateView(index->views().at(viewIndex)); } void MainWindowPrivate::gotoNextWindow() { gotoPrevNextWindow(true); } void MainWindowPrivate::gotoPreviousWindow() { gotoPrevNextWindow(false); } +void MainWindowPrivate::selectPrevItem() +{ + auto actionListener = qobject_cast( + Core::self()->uiControllerInternal()->activeToolViewActionListener()); + if (actionListener) { + actionListener->selectPreviousItem(); + } +} + +void MainWindowPrivate::selectNextItem() +{ + auto actionListener = qobject_cast( + Core::self()->uiControllerInternal()->activeToolViewActionListener()); + if (actionListener) { + actionListener->selectNextItem(); + } +} + void MainWindowPrivate::newToolbarConfig() { m_mainWindow->applyMainWindowSettings( KConfigGroup(KSharedConfig::openConfig(), "MainWindow") ); } void MainWindowPrivate::settingsDialog() { Core::self()->uiControllerInternal()->showSettingsDialog(); } void MainWindowPrivate::newWindow() { Core::self()->uiController()->switchToArea(m_mainWindow->area()->objectName(), UiController::NewWindow); } void MainWindowPrivate::splitHorizontal() { split(Qt::Vertical); } void MainWindowPrivate::splitVertical() { split(Qt::Horizontal); } void MainWindowPrivate::split(Qt::Orientation orientation) { if (!m_mainWindow->area()) return; Sublime::View *view = m_mainWindow->activeView(); if (!view) return; Sublime::View *newView = view->document()->createView(); m_mainWindow->area()->addView(newView, view, orientation); m_mainWindow->activateView(newView); } static void gotoPrevNextSplit(bool next) { UiController* ui = Core::self()->uiControllerInternal(); if( !ui->activeSublimeWindow() ) return; Sublime::Area* area = ui->activeSublimeWindow()->area(); if (!area) return; QList topViews = ui->activeSublimeWindow()->getTopViews(); Sublime::View *activeView = ui->activeSublimeWindow()->activeView(); if (!activeView) return; int viewIndex = topViews.indexOf(activeView); viewIndex = next ? viewIndex + 1 : viewIndex -1; if (viewIndex < 0) viewIndex = topViews.count() - 1; else if (viewIndex >= topViews.count()) viewIndex = 0; if (viewIndex >= 0 && viewIndex < topViews.count()) ui->activeSublimeWindow()->activateView(topViews.at(viewIndex)); } void MainWindowPrivate::gotoNextSplit() { gotoPrevNextSplit(true); } void MainWindowPrivate::gotoPreviousSplit() { gotoPrevNextSplit(false); } void MainWindowPrivate::toggleFullScreen(bool fullScreen) { KToggleFullScreenAction::setFullScreen( m_mainWindow, fullScreen ); } void MainWindowPrivate::fileNew() { Core::self()->documentControllerInternal()->openDocument(DocumentController::nextEmptyDocumentUrl()); } void MainWindowPrivate::viewAddNewToolView() { Core::self()->uiControllerInternal()->selectNewToolViewToAdd(m_mainWindow); } void MainWindowPrivate::quitAll() { s_quitRequested = true; QApplication::closeAllWindows(); //if (Core::self()->documentController()->saveAllDocuments(IDocument::Default)) // return qApp->exit(); s_quitRequested = false; } void MainWindowPrivate::configureNotifications() { KNotifyConfigWidget::configure(m_mainWindow); } void MainWindowPrivate::showAboutPlatform() { KAboutApplicationDialog dlg(Core::self()->aboutData(), m_mainWindow ); dlg.exec(); } void MainWindowPrivate::showLoadedPlugins() { LoadedPluginsDialog dlg(m_mainWindow); dlg.exec(); } void MainWindowPrivate::contextMenuFileNew() { m_mainWindow->activateView(m_tabView); fileNew(); } void MainWindowPrivate::contextMenuSplitHorizontal() { m_mainWindow->activateView(m_tabView); splitHorizontal(); } void MainWindowPrivate::contextMenuSplitVertical() { m_mainWindow->activateView(m_tabView); splitVertical(); } void MainWindowPrivate::reloadAll() { foreach ( IDocument* doc, Core::self()->documentController()->openDocuments() ) { doc->reload(); } } } diff --git a/shell/mainwindow_p.cpp b/shell/mainwindow_p.cpp index 12723ea5f8..516bc01ac9 100644 --- a/shell/mainwindow_p.cpp +++ b/shell/mainwindow_p.cpp @@ -1,446 +1,458 @@ /* This file is part of the KDevelop project Copyright 2002 Falk Brettschneider Copyright 2003 John Firebaugh Copyright 2006 Adam Treat Copyright 2006, 2007 Alexander Dymo 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 "mainwindow_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core.h" #include "partdocument.h" #include "partcontroller.h" #include "uicontroller.h" #include "statusbar.h" #include "mainwindow.h" #include "textdocument.h" #include "sessioncontroller.h" #include "debug.h" #include #include #include #include #include namespace KDevelop { bool MainWindowPrivate::s_quitRequested = false; MainWindowPrivate::MainWindowPrivate(MainWindow *mainWindow) : m_mainWindow(mainWindow), m_statusBar(0), lastXMLGUIClientView(0), m_changingActiveView(false) { } void MainWindowPrivate::setupGui() { m_statusBar = new KDevelop::StatusBar(m_mainWindow); setupStatusBar(); } void MainWindowPrivate::setupStatusBar() { QWidget *location = m_mainWindow->statusBarLocation(); if (m_statusBar) location->layout()->addWidget(m_statusBar); } void MainWindowPrivate::addPlugin( IPlugin *plugin ) { qCDebug(SHELL) << "add plugin" << plugin << plugin->componentName(); Q_ASSERT( plugin ); //The direct plugin client can only be added to the first mainwindow if(m_mainWindow == Core::self()->uiControllerInternal()->mainWindows()[0]) m_mainWindow->guiFactory()->addClient( plugin ); Q_ASSERT(!m_pluginCustomClients.contains(plugin)); KXMLGUIClient* ownClient = plugin->createGUIForMainWindow(m_mainWindow); if(ownClient) { m_pluginCustomClients[plugin] = ownClient; connect(plugin, &IPlugin::destroyed, this, &MainWindowPrivate::pluginDestroyed); m_mainWindow->guiFactory()->addClient(ownClient); } } void MainWindowPrivate::pluginDestroyed(QObject* pluginObj) { IPlugin* plugin = static_cast(pluginObj); Q_ASSERT(m_pluginCustomClients.contains(plugin)); m_mainWindow->guiFactory()->removeClient( m_pluginCustomClients[plugin] ); delete m_pluginCustomClients[plugin]; m_pluginCustomClients.remove(plugin); } MainWindowPrivate::~MainWindowPrivate() { foreach(KXMLGUIClient* client, m_pluginCustomClients.values()) delete client; } void MainWindowPrivate::removePlugin( IPlugin *plugin ) { Q_ASSERT( plugin ); if(m_pluginCustomClients.contains(plugin)) { m_mainWindow->guiFactory()->removeClient( m_pluginCustomClients[plugin] ); delete m_pluginCustomClients[plugin]; m_pluginCustomClients.remove(plugin); disconnect(plugin, &IPlugin::destroyed, this, &MainWindowPrivate::pluginDestroyed); } m_mainWindow->guiFactory()->removeClient( plugin ); } void MainWindowPrivate::activePartChanged(KParts::Part *part) { if ( Core::self()->uiController()->activeMainWindow() == m_mainWindow) m_mainWindow->createGUI(part); } void MainWindowPrivate::changeActiveView(Sublime::View *view) { //disable updates on a window to avoid toolbar flickering on xmlgui client change Sublime::HoldUpdates s(m_mainWindow); mergeView(view); if(!view) return; IDocument *doc = dynamic_cast(view->document()); if (doc) { doc->activate(view, m_mainWindow); } else { //activated view is not a part document so we need to remove active part gui ///@todo adymo: only this window needs to remove GUI // KParts::Part *activePart = Core::self()->partController()->activePart(); // if (activePart) // guiFactory()->removeClient(activePart); } } void MainWindowPrivate::mergeView(Sublime::View* view) { PushPositiveValue block(m_changingActiveView, true); // If the previous view was KXMLGUIClient, remove its actions // In the case that that view was removed, lastActiveView // will auto-reset, and xmlguifactory will disconnect that // client, I think. if (lastXMLGUIClientView) { qCDebug(SHELL) << "clearing last XML GUI client" << lastXMLGUIClientView; m_mainWindow->guiFactory()->removeClient(dynamic_cast(lastXMLGUIClientView)); disconnect (lastXMLGUIClientView, &QWidget::destroyed, this, 0); lastXMLGUIClientView = NULL; } if (!view) return; QWidget* viewWidget = view->widget(); Q_ASSERT(viewWidget); qCDebug(SHELL) << "changing active view to" << view << "doc" << view->document() << "mw" << m_mainWindow; // If the new view is KXMLGUIClient, add it. if (KXMLGUIClient* c = dynamic_cast(viewWidget)) { qCDebug(SHELL) << "setting new XMLGUI client" << viewWidget; lastXMLGUIClientView = viewWidget; m_mainWindow->guiFactory()->addClient(c); connect(viewWidget, &QWidget::destroyed, this, &MainWindowPrivate::xmlguiclientDestroyed); } } void MainWindowPrivate::xmlguiclientDestroyed(QObject* obj) { /* We're informed the QWidget for the active view that is also KXMLGUIclient is dying. KXMLGUIFactory will not like deleted clients, really. Unfortunately, there's nothing we can do at this point. For example, KateView derives from QWidget and KXMLGUIClient. The destroyed() signal is emitted by ~QWidget. At this point, event attempt to cross-cast to KXMLGUIClient is undefined behaviour. We hope to catch view deletion a bit later, but if we fail, we better report it now, rather than get a weird crash a bit later. */ Q_ASSERT(obj == lastXMLGUIClientView); Q_ASSERT(false && "xmlgui clients management is messed up"); Q_UNUSED(obj); } void MainWindowPrivate::setupActions() { connect(Core::self()->sessionController(), &SessionController::quitSession, this, &MainWindowPrivate::quitAll); QAction* action; QString app = qApp->applicationName(); QString text = i18nc( "%1 = application name", "Configure %1", app ); action = KStandardAction::preferences( this, SLOT(settingsDialog()), actionCollection()); action->setToolTip( text ); action->setWhatsThis( i18n( "Lets you customize %1.", app ) ); action = KStandardAction::configureNotifications(this, SLOT(configureNotifications()), actionCollection()); action->setText( i18n("Configure Notifications...") ); action->setToolTip( i18nc("@info:tooltip", "Configure notifications") ); action->setWhatsThis( i18nc( "@info:whatsthis", "Shows a dialog that lets you configure notifications." ) ); action = actionCollection()->addAction( "about_platform", this, SLOT(showAboutPlatform()) ); action->setText( i18n("About KDevelop Platform") ); action->setStatusTip( i18n("Show Information about KDevelop Platform") ); action->setWhatsThis( i18nc( "@info:whatsthis", "Shows a dialog with information about KDevelop Platform." ) ); action = actionCollection()->addAction( "loaded_plugins", this, SLOT(showLoadedPlugins()) ); action->setText( i18n("Loaded Plugins") ); action->setStatusTip( i18n("Show a list of all loaded plugins") ); action->setWhatsThis( i18nc( "@info:whatsthis", "Shows a dialog with information about all loaded plugins." ) ); action = actionCollection()->addAction( "view_next_window" ); action->setText( i18n( "&Next Window" ) ); connect( action, &QAction::triggered, this, &MainWindowPrivate::gotoNextWindow ); actionCollection()->setDefaultShortcut(action, Qt::ALT + Qt::SHIFT + Qt::Key_Right ); action->setToolTip( i18nc( "@info:tooltip", "Next window" ) ); action->setWhatsThis( i18nc( "@info:whatsthis", "Switches to the next window." ) ); action->setIcon(QIcon::fromTheme("go-next")); action = actionCollection()->addAction( "view_previous_window" ); action->setText( i18n( "&Previous Window" ) ); connect( action, &QAction::triggered, this, &MainWindowPrivate::gotoPreviousWindow ); actionCollection()->setDefaultShortcut(action, Qt::ALT + Qt::SHIFT + Qt::Key_Left ); action->setToolTip( i18nc( "@info:tooltip", "Previous window" ) ); action->setWhatsThis( i18nc( "@info:whatsthis", "Switches to the previous window." ) ); action->setIcon(QIcon::fromTheme("go-previous")); + action = actionCollection()->addAction("next_error"); + action->setText(i18n("Jump to Next Outputmark")); + actionCollection()->setDefaultShortcut( action, QKeySequence(Qt::Key_F4) ); + action->setIcon(QIcon::fromTheme("arrow-right")); + connect(action, &QAction::triggered, this, &MainWindowPrivate::selectNextItem); + + action = actionCollection()->addAction("prev_error"); + action->setText(i18n("Jump to Previous Outputmark")); + actionCollection()->setDefaultShortcut( action, QKeySequence(Qt::SHIFT | Qt::Key_F4) ); + action->setIcon(QIcon::fromTheme("arrow-left")); + connect(action, &QAction::triggered, this, &MainWindowPrivate::selectPrevItem); + action = actionCollection()->addAction( "split_horizontal" ); action->setIcon(QIcon::fromTheme( "view-split-top-bottom" )); action->setText( i18n( "Split View &Top/Bottom" ) ); actionCollection()->setDefaultShortcut(action, Qt::CTRL + Qt::SHIFT + Qt::Key_T ); connect( action, &QAction::triggered, this, &MainWindowPrivate::splitHorizontal ); action->setToolTip( i18nc( "@info:tooltip", "Split horizontal" ) ); action->setWhatsThis( i18nc( "@info:whatsthis", "Splits the current view horizontally." ) ); action = actionCollection()->addAction( "split_vertical" ); action->setIcon(QIcon::fromTheme( "view-split-left-right" )); action->setText( i18n( "Split View &Left/Right" ) ); actionCollection()->setDefaultShortcut(action, Qt::CTRL + Qt::SHIFT + Qt::Key_L ); connect( action, &QAction::triggered, this, &MainWindowPrivate::splitVertical ); action->setToolTip( i18nc( "@info:tooltip", "Split vertical" ) ); action->setWhatsThis( i18nc( "@info:whatsthis", "Splits the current view vertically." ) ); action = actionCollection()->addAction( "view_next_split" ); action->setText( i18n( "&Next Split View" ) ); connect( action, &QAction::triggered, this, &MainWindowPrivate::gotoNextSplit ); actionCollection()->setDefaultShortcut(action, Qt::CTRL + Qt::SHIFT + Qt::Key_N ); action->setToolTip( i18nc( "@info:tooltip", "Next split view" ) ); action->setWhatsThis( i18nc( "@info:whatsthis", "Switches to the next split view." ) ); action->setIcon(QIcon::fromTheme("go-next")); action = actionCollection()->addAction( "view_previous_split" ); action->setText( i18n( "&Previous Split View" ) ); connect( action, &QAction::triggered, this, &MainWindowPrivate::gotoPreviousSplit ); actionCollection()->setDefaultShortcut(action, Qt::CTRL + Qt::SHIFT + Qt::Key_P ); action->setToolTip( i18nc( "@info:tooltip", "Previous split view" ) ); action->setWhatsThis( i18nc( "@info:whatsthis", "Switches to the previous split view." ) ); action->setIcon(QIcon::fromTheme("go-previous")); action = KStandardAction::fullScreen( this, SLOT(toggleFullScreen(bool)), m_mainWindow, actionCollection() ); action = actionCollection()->addAction( "file_new" ); action->setIcon(QIcon::fromTheme("document-new")); actionCollection()->setDefaultShortcut(action, Qt::CTRL + Qt::Key_N ); action->setText( i18n( "&New" ) ); action->setIconText( i18nc( "Shorter Text for 'New File' shown in the toolbar", "New") ); connect( action, &QAction::triggered, this, &MainWindowPrivate::fileNew ); action->setToolTip( i18nc( "@info:tooltip", "New file" ) ); action->setWhatsThis( i18nc( "@info:whatsthis", "Creates an empty file." ) ); action = actionCollection()->addAction( "add_toolview" ); action->setIcon(QIcon::fromTheme("window-new")); actionCollection()->setDefaultShortcut(action, Qt::CTRL + Qt::SHIFT + Qt::Key_V ); action->setText( i18n( "&Add Tool View..." ) ); connect( action, &QAction::triggered, this, &MainWindowPrivate::viewAddNewToolView ); action->setToolTip( i18nc( "@info:tooltip", "Add tool view" ) ); action->setWhatsThis( i18nc( "@info:whatsthis", "Adds a new tool view to this window." ) ); } void MainWindowPrivate::toggleArea(bool b) { if (!b) return; QAction* action = qobject_cast(sender()); if (!action) return; m_mainWindow->controller()->showArea(action->data().toString(), m_mainWindow); } KActionCollection * MainWindowPrivate::actionCollection() { return m_mainWindow->actionCollection(); } bool MainWindowPrivate::applicationQuitRequested() const { return s_quitRequested; } void MainWindowPrivate::registerStatus(QObject* status) { m_statusBar->registerStatus(status); } void MainWindowPrivate::showErrorMessage(QString message, int timeout) { m_statusBar->showErrorMessage(message, timeout); } void MainWindowPrivate::tabContextMenuRequested(Sublime::View* view, QMenu* menu) { m_tabView = view; QAction* action; action = menu->addAction(QIcon::fromTheme("view-split-top-bottom"), i18n("Split View Top/Bottom")); connect(action, &QAction::triggered, this, &MainWindowPrivate::contextMenuSplitHorizontal); action = menu->addAction(QIcon::fromTheme("view-split-left-right"), i18n("Split View Left/Right")); connect(action, &QAction::triggered, this, &MainWindowPrivate::contextMenuSplitVertical); menu->addSeparator(); action = menu->addAction(QIcon::fromTheme("document-new"), i18n("New File")); connect(action, &QAction::triggered, this, &MainWindowPrivate::contextMenuFileNew); if (view) { if (TextDocument* doc = dynamic_cast(view->document())) { action = menu->addAction(QIcon::fromTheme("view-refresh"), i18n("Reload")); connect(action, &QAction::triggered, doc, &TextDocument::reload); action = menu->addAction(QIcon::fromTheme("view-refresh"), i18n("Reload All")); connect(action, &QAction::triggered, this, &MainWindowPrivate::reloadAll); } } } void MainWindowPrivate::tabToolTipRequested(Sublime::View* view, Sublime::Container* container, int tab) { if (m_tabTooltip.second) { if (m_tabTooltip.first == view) { // tooltip already shown, don't do anything. prevents flicker when moving mouse over same tab return; } else { m_tabTooltip.second.data()->close(); } } DUChainReadLocker lock; Sublime::UrlDocument* urlDoc = dynamic_cast(view->document()); if (urlDoc) { TopDUContext* top = DUChainUtils::standardContextForUrl(urlDoc->url()); if (top) { if ( QWidget* navigationWidget = top->createNavigationWidget() ) { NavigationToolTip* tooltip = new KDevelop::NavigationToolTip(m_mainWindow, QCursor::pos() + QPoint(20, 20), navigationWidget); tooltip->resize(navigationWidget->sizeHint() + QSize(10, 10)); tooltip->addExtendRect(container->tabRect(tab)); m_tabTooltip.first = view; m_tabTooltip.second = tooltip; ActiveToolTip::showToolTip(m_tabTooltip.second.data()); } } } } void MainWindowPrivate::dockBarContextMenuRequested(Qt::DockWidgetArea area, const QPoint& position) { QMenu menu; menu.addSection(QIcon::fromTheme("window-new"), i18n("Add Tool View")); QMap factories = Core::self()->uiControllerInternal()->factoryDocuments(); QMap actionToFactory; if ( !factories.isEmpty() ) { // sorted actions QMap actionMap; for (QMap::const_iterator it = factories.constBegin(); it != factories.constEnd(); ++it) { QAction* action = new QAction(it.value()->statusIcon(), it.value()->title(), &menu); action->setIcon(it.value()->statusIcon()); if (!it.key()->allowMultiple() && Core::self()->uiControllerInternal()->toolViewPresent(it.value(), m_mainWindow->area())) { action->setDisabled(true); } actionToFactory.insert(action, it.key()); actionMap[action->text()] = action; } menu.addActions(actionMap.values()); } QAction* triggered = menu.exec(position); if ( !triggered ) { return; } Core::self()->uiControllerInternal()->addToolViewToDockArea( actionToFactory[triggered], area ); } bool MainWindowPrivate::changingActiveView() const { return m_changingActiveView; } } #include "mainwindow_actions.cpp" diff --git a/shell/mainwindow_p.h b/shell/mainwindow_p.h index affb1f624f..9cc4b235c2 100644 --- a/shell/mainwindow_p.h +++ b/shell/mainwindow_p.h @@ -1,147 +1,150 @@ /* This file is part of the KDevelop project Copyright 2002 Falk Brettschneider Copyright 2003 John Firebaugh Copyright 2006 Adam Treat Copyright 2006, 2007 Alexander Dymo 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. */ #ifndef KDEVPLATFORM_MAINWINDOW_PRIVATE_H #define KDEVPLATFORM_MAINWINDOW_PRIVATE_H #include #include #include #include #include class KActionCollection; class QMenu; namespace Sublime { class View; class Container; } namespace KParts { class Part; } namespace KTextEditor { class View; } namespace KDevelop { class IPlugin; class MainWindow; class StatusBar; class MainWindowPrivate: public QObject { Q_OBJECT public: MainWindowPrivate(MainWindow *mainWindow); ~MainWindowPrivate(); QPointer centralPlugin; void setupActions(); void setupGui(); void setupStatusBar(); void registerStatus(QObject*); void tabContextMenuRequested(Sublime::View *view, QMenu* menu); void tabToolTipRequested(Sublime::View* view, Sublime::Container* container, int tab); void dockBarContextMenuRequested(Qt::DockWidgetArea area, const QPoint& position); public Q_SLOTS: void addPlugin( KDevelop::IPlugin *plugin ); void removePlugin( KDevelop::IPlugin *plugin ); void activePartChanged(KParts::Part *part); void mergeView(Sublime::View *view); void changeActiveView(Sublime::View *view); void xmlguiclientDestroyed(QObject* obj); //actions void fileNew(); void gotoNextWindow(); void gotoPreviousWindow(); + void selectPrevItem(); + void selectNextItem(); + void viewAddNewToolView(); void newWindow(); void splitHorizontal(); void splitVertical(); void split(Qt::Orientation orientation); void toggleFullScreen(bool fullScreen); void gotoNextSplit(); void gotoPreviousSplit(); void newToolbarConfig(); void settingsDialog(); void quitAll(); // void fixToolbar(); ///Returns true if we're currently changing the active view through changeActiveView() bool changingActiveView() const ; bool applicationQuitRequested() const; void configureNotifications(); void showAboutPlatform(); void showLoadedPlugins(); void toggleArea(bool b); void showErrorMessage(QString message, int timeout); void pluginDestroyed(QObject*); /// the following slots always activate the m_tabView before calling the normal slot above /// @see m_tabView /// @see tabContextMenuRequested void contextMenuFileNew(); void contextMenuSplitHorizontal(); void contextMenuSplitVertical(); /// reload all open documents void reloadAll(); private: KActionCollection *actionCollection(); MainWindow *m_mainWindow; StatusBar* m_statusBar; QWidget* lastXMLGUIClientView; QPointer m_workingSetCornerWidget; QMap m_pluginCustomClients; static bool s_quitRequested; bool m_changingActiveView; /// the view of the tab that got it's context menu connected Sublime::View* m_tabView; QPair > m_tabTooltip; }; } #endif diff --git a/shell/uicontroller.cpp b/shell/uicontroller.cpp index 0f2270be2e..7d93878a54 100644 --- a/shell/uicontroller.cpp +++ b/shell/uicontroller.cpp @@ -1,725 +1,738 @@ /*************************************************************************** * Copyright 2007 Alexander Dymo * * * * 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 "uicontroller.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include + #include "core.h" #include "configpage.h" #include "configdialog.h" #include "debug.h" #include "editorconfigpage.h" #include "shellextension.h" #include "partcontroller.h" #include "plugincontroller.h" #include "mainwindow.h" #include "partdocument.h" #include "textdocument.h" #include "documentcontroller.h" #include "assistantpopup.h" #include #include #include "workingsetcontroller.h" #include "workingsets/workingset.h" #include "settings/bgpreferences.h" #include "settings/ccpreferences.h" #include "settings/environmentpreferences.h" #include "settings/pluginpreferences.h" #include "settings/projectpreferences.h" #include "settings/sourceformattersettings.h" #include "settings/uipreferences.h" #include "settings/templateconfig.h" namespace KDevelop { class UiControllerPrivate { public: UiControllerPrivate(UiController *controller) : areasRestored(false), m_controller(controller) { if (Core::self()->workingSetControllerInternal()) Core::self()->workingSetControllerInternal()->initializeController(m_controller); QMap desired; desired["org.kdevelop.ClassBrowserView"] = Sublime::Left; desired["org.kdevelop.DocumentsView"] = Sublime::Left; desired["org.kdevelop.ProjectsView"] = Sublime::Left; desired["org.kdevelop.FileManagerView"] = Sublime::Left; desired["org.kdevelop.ProblemReporterView"] = Sublime::Bottom; desired["org.kdevelop.OutputView"] = Sublime::Bottom; desired["org.kdevelop.ContextBrowser"] = Sublime::Bottom; desired["org.kdevelop.KonsoleView"] = Sublime::Bottom; desired["org.kdevelop.SnippetView"] = Sublime::Right; desired["org.kdevelop.ExternalScriptView"] = Sublime::Right; Sublime::Area* a = new Sublime::Area(m_controller, "code", i18n("Code")); a->setDesiredToolViews(desired); a->setIconName("document-edit"); m_controller->addDefaultArea(a); desired.clear(); desired["org.kdevelop.debugger.VariablesView"] = Sublime::Left; desired["org.kdevelop.debugger.BreakpointsView"] = Sublime::Bottom; desired["org.kdevelop.debugger.StackView"] = Sublime::Bottom; desired["org.kdevelop.debugger.ConsoleView"] = Sublime::Bottom; desired["org.kdevelop.KonsoleView"] = Sublime::Bottom; a = new Sublime::Area(m_controller, "debug", i18n("Debug")); a->setDesiredToolViews(desired); a->setIconName("tools-report-bug"); m_controller->addDefaultArea(a); desired.clear(); desired["org.kdevelop.ProjectsView"] = Sublime::Left; desired["org.kdevelop.PatchReview"] = Sublime::Bottom; a = new Sublime::Area(m_controller, "review", i18n("Review")); a->setDesiredToolViews(desired); a->setIconName("applications-engineering"); m_controller->addDefaultArea(a); if(!(Core::self()->setupFlags() & Core::NoUi)) { defaultMainWindow = new MainWindow(m_controller); m_controller->addMainWindow(defaultMainWindow); activeSublimeWindow = defaultMainWindow; } else { activeSublimeWindow = defaultMainWindow = 0; } m_assistantTimer.setSingleShot(true); m_assistantTimer.setInterval(100); } void widgetChanged(QWidget*, QWidget* now) { if (now) { Sublime::MainWindow* win = qobject_cast(now->window()); if( win ) { activeSublimeWindow = win; } } } Core *core; MainWindow* defaultMainWindow; QMap factoryDocuments; Sublime::MainWindow* activeSublimeWindow; bool areasRestored; - //Currently shown assistant popup. + /// Currently shown assistant popup. QPointer currentShownAssistant; + /// QWidget implementing IToolViewActionListener interface, or null + QPointer activeActionListener; QTimer m_assistantTimer; private: UiController *m_controller; }; class UiToolViewFactory: public Sublime::ToolFactory { public: UiToolViewFactory(IToolViewFactory *factory): m_factory(factory) {} ~UiToolViewFactory() { delete m_factory; } virtual QWidget* create(Sublime::ToolDocument *doc, QWidget *parent = 0) { Q_UNUSED( doc ); return m_factory->create(parent); } virtual QList< QAction* > contextMenuActions(QWidget* viewWidget) const { return m_factory->contextMenuActions( viewWidget ); } QList toolBarActions( QWidget* viewWidget ) const { return m_factory->toolBarActions( viewWidget ); } QString id() const { return m_factory->id(); } private: IToolViewFactory *m_factory; }; class ViewSelectorItem: public QListWidgetItem { public: ViewSelectorItem(const QString &text, QListWidget *parent = 0, int type = Type) :QListWidgetItem(text, parent, type) {} IToolViewFactory *factory; }; class NewToolViewListWidget: public QListWidget { Q_OBJECT public: NewToolViewListWidget(MainWindow *mw, QWidget* parent = 0) :QListWidget(parent), m_mw(mw) { connect(this, &NewToolViewListWidget::doubleClicked, this, &NewToolViewListWidget::addNewToolViewByDoubleClick); } Q_SIGNALS: void addNewToolView(MainWindow *mw, QListWidgetItem *item); private Q_SLOTS: void addNewToolViewByDoubleClick(QModelIndex index) { QListWidgetItem *item = itemFromIndex(index); // Disable item so that the toolview can not be added again. item->setFlags(item->flags() & ~Qt::ItemIsEnabled); emit addNewToolView(m_mw, item); } private: MainWindow *m_mw; }; UiController::UiController(Core *core) :Sublime::Controller(0), IUiController(), d(new UiControllerPrivate(this)) { setObjectName("UiController"); d->core = core; if (!defaultMainWindow() || (Core::self()->setupFlags() & Core::NoUi)) return; connect(qApp, &QApplication::focusChanged, this, [&] (QWidget* old, QWidget* now) { d->widgetChanged(old, now); } ); setupActions(); } UiController::~UiController() { delete d; } void UiController::setupActions() { } void UiController::mainWindowDeleted(MainWindow* mw) { if (d->defaultMainWindow == mw) d->defaultMainWindow = 0L; if (d->activeSublimeWindow == mw) d->activeSublimeWindow = 0L; } // FIXME: currently, this always create new window. Probably, // should just rename it. void UiController::switchToArea(const QString &areaName, SwitchMode switchMode) { if (switchMode == ThisWindow) { showArea(areaName, activeSublimeWindow()); return; } MainWindow *main = new MainWindow(this); addMainWindow(main); showArea(areaName, main); main->initialize(); // WTF? First, enabling this code causes crashes since we // try to disconnect some already-deleted action, or something. // Second, this code will disconnection the clients from guiFactory // of the previous main window. Ick! #if 0 //we need to add all existing guiclients to the new mainwindow //@todo adymo: add only ones that belong to the area (when the area code is there) foreach (KXMLGUIClient *client, oldMain->guiFactory()->clients()) main->guiFactory()->addClient(client); #endif main->show(); } QWidget* UiController::findToolView(const QString& name, IToolViewFactory *factory, FindFlags flags) { if(!d->areasRestored || !activeArea()) return 0; QList< Sublime::View* > views = activeArea()->toolViews(); foreach(Sublime::View* view, views) { Sublime::ToolDocument *doc = dynamic_cast(view->document()); if(doc && doc->title() == name && view->widget()) { if(flags & Raise) view->requestRaise(); return view->widget(); } } QWidget* ret = 0; if(flags & Create) { if(!d->factoryDocuments.contains(factory)) d->factoryDocuments[factory] = new Sublime::ToolDocument(name, this, new UiToolViewFactory(factory)); Sublime::ToolDocument *doc = d->factoryDocuments[factory]; Sublime::View* view = addToolViewToArea(factory, doc, activeArea()); if(view) ret = view->widget(); if(flags & Raise) findToolView(name, factory, Raise); } return ret; } void UiController::raiseToolView(QWidget* toolViewWidget) { if(!d->areasRestored) return; QList< Sublime::View* > views = activeArea()->toolViews(); foreach(Sublime::View* view, views) { if(view->widget() == toolViewWidget) { view->requestRaise(); return; } } } void UiController::addToolView(const QString & name, IToolViewFactory *factory) { if (!factory) return; qCDebug(SHELL) ; Sublime::ToolDocument *doc = new Sublime::ToolDocument(name, this, new UiToolViewFactory(factory)); d->factoryDocuments[factory] = doc; /* Until areas are restored, we don't know which views should be really added, and which not, so we just record view availability. */ if (d->areasRestored) { foreach (Sublime::Area* area, allAreas()) { addToolViewToArea(factory, doc, area); } } } void KDevelop::UiController::raiseToolView(Sublime::View * view) { foreach( Sublime::Area* area, allAreas() ) { if( area->toolViews().contains( view ) ) area->raiseToolView( view ); } + + if (qobject_cast(view->widget())) { + d->activeActionListener = view->widget(); + } } void KDevelop::UiController::removeToolView(IToolViewFactory *factory) { if (!factory) return; qCDebug(SHELL) ; //delete the tooldocument Sublime::ToolDocument *doc = d->factoryDocuments[factory]; ///@todo adymo: on document deletion all its views shall be also deleted foreach (Sublime::View *view, doc->views()) { foreach (Sublime::Area *area, allAreas()) if (area->removeToolView(view)) view->deleteLater(); } d->factoryDocuments.remove(factory); delete doc; } Sublime::Area *UiController::activeArea() { Sublime::MainWindow *m = activeSublimeWindow(); if (m) return activeSublimeWindow()->area(); return 0; } Sublime::MainWindow *UiController::activeSublimeWindow() { return d->activeSublimeWindow; } MainWindow *UiController::defaultMainWindow() { return d->defaultMainWindow; } void UiController::initialize() { defaultMainWindow()->initialize(); } void UiController::cleanup() { foreach (Sublime::MainWindow* w, mainWindows()) w->saveSettings(); saveAllAreas(KSharedConfig::openConfig()); } void UiController::selectNewToolViewToAdd(MainWindow *mw) { if (!mw || !mw->area()) return; QDialog *dia = new QDialog(mw); dia->setWindowTitle(i18n("Select Tool View to Add")); auto mainLayout = new QVBoxLayout(dia); NewToolViewListWidget *list = new NewToolViewListWidget(mw, dia); list->setSelectionMode(QAbstractItemView::ExtendedSelection); list->setSortingEnabled(true); for (QMap::const_iterator it = d->factoryDocuments.constBegin(); it != d->factoryDocuments.constEnd(); ++it) { ViewSelectorItem *item = new ViewSelectorItem(it.value()->title(), list); item->factory = it.key(); if (!item->factory->allowMultiple() && toolViewPresent(it.value(), mw->area())) { // Disable item if the toolview is already present. item->setFlags(item->flags() & ~Qt::ItemIsEnabled); } list->addItem(item); } list->setFocus(); connect(list, &NewToolViewListWidget::addNewToolView, this, &UiController::addNewToolView); mainLayout->addWidget(list); auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); auto okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); dia->connect(buttonBox, &QDialogButtonBox::accepted, dia, &QDialog::accept); dia->connect(buttonBox, &QDialogButtonBox::rejected, dia, &QDialog::reject); mainLayout->addWidget(buttonBox); if (dia->exec() == QDialog::Accepted) { foreach (QListWidgetItem* item, list->selectedItems()) { addNewToolView(mw, item); } } delete dia; } void UiController::addNewToolView(MainWindow *mw, QListWidgetItem* item) { ViewSelectorItem *current = static_cast(item); Sublime::ToolDocument *doc = d->factoryDocuments[current->factory]; Sublime::View *view = doc->createView(); mw->area()->addToolView(view, Sublime::dockAreaToPosition(current->factory->defaultPosition())); current->factory->viewCreated(view); } void UiController::showSettingsDialog() { auto editorConfigPage = new EditorConfigPage(activeMainWindow()); auto configPages = QVector { new UiPreferences(activeMainWindow()), new PluginPreferences(activeMainWindow()), new SourceFormatterSettings(activeMainWindow()), new ProjectPreferences(activeMainWindow()), new EnvironmentPreferences(QString(), activeMainWindow()), new CCPreferences(activeMainWindow()), new BGPreferences(activeMainWindow()), new TemplateConfig(activeMainWindow()), editorConfigPage }; ConfigDialog cfgDlg(configPages, activeMainWindow()); auto addPluginPages = [&](IPlugin* plugin) { for (int i = 0, numPages = plugin->configPages(); i < numPages; ++i) { // insert them before the editor config page cfgDlg.addConfigPage(plugin->configPage(i, &cfgDlg), editorConfigPage); } }; for (IPlugin* plugin : ICore::self()->pluginController()->loadedPlugins()) { addPluginPages(plugin); } // TODO: only load settings if a UI related page was changed? connect(&cfgDlg, &ConfigDialog::configSaved, activeSublimeWindow(), &Sublime::MainWindow::loadSettings); // make sure that pages get added whenever a new plugin is loaded (probably from the plugin selection dialog) // removal on plugin unload is already handled in ConfigDialog connect(ICore::self()->pluginController(), &IPluginController::pluginLoaded, &cfgDlg, addPluginPages); cfgDlg.exec(); } Sublime::Controller* UiController::controller() { return this; } KParts::MainWindow *UiController::activeMainWindow() { return activeSublimeWindow(); } void UiController::saveArea(Sublime::Area * area, KConfigGroup & group) { area->save(group); if (!area->workingSet().isEmpty()) { WorkingSet* set = Core::self()->workingSetControllerInternal()->getWorkingSet(area->workingSet()); set->saveFromArea(area, area->rootIndex()); } } void UiController::loadArea(Sublime::Area * area, const KConfigGroup & group) { area->load(group); if (!area->workingSet().isEmpty()) { WorkingSet* set = Core::self()->workingSetControllerInternal()->getWorkingSet(area->workingSet()); Q_ASSERT(set->isConnected(area)); Q_UNUSED(set); } } void UiController::saveAllAreas(KSharedConfigPtr config) { KConfigGroup uiConfig(config, "User Interface"); int wc = mainWindows().size(); uiConfig.writeEntry("Main Windows Count", wc); for (int w = 0; w < wc; ++w) { KConfigGroup mainWindowConfig(&uiConfig, QStringLiteral("Main Window %1").arg(w)); foreach (Sublime::Area* defaultArea, defaultAreas()) { // FIXME: using object name seems ugly. QString type = defaultArea->objectName(); Sublime::Area* area = this->area(w, type); KConfigGroup areaConfig(&mainWindowConfig, "Area " + type); areaConfig.deleteGroup(); areaConfig.writeEntry("id", type); saveArea(area, areaConfig); areaConfig.sync(); } } uiConfig.sync(); } void UiController::loadAllAreas(KSharedConfigPtr config) { KConfigGroup uiConfig(config, "User Interface"); int wc = uiConfig.readEntry("Main Windows Count", 1); /* It is expected the main windows are restored before restoring areas. */ if (wc > mainWindows().size()) wc = mainWindows().size(); QList changedAreas; /* Offer all toolviews to the default areas. */ foreach (Sublime::Area *area, defaultAreas()) { QMap::const_iterator i, e; for (i = d->factoryDocuments.constBegin(), e = d->factoryDocuments.constEnd(); i != e; ++i) { addToolViewIfWanted(i.key(), i.value(), area); } } /* Restore per-windows areas. */ for (int w = 0; w < wc; ++w) { KConfigGroup mainWindowConfig(&uiConfig, QStringLiteral("Main Window %1").arg(w)); Sublime::MainWindow *mw = mainWindows()[w]; /* We loop over default areas. This means that if the config file has an area of some type that is not in default set, we'd just ignore it. I think it's fine -- the model were a given mainwindow can has it's own area types not represented in the default set is way too complex. */ foreach (Sublime::Area* defaultArea, defaultAreas()) { QString type = defaultArea->objectName(); Sublime::Area* area = this->area(w, type); KConfigGroup areaConfig(&mainWindowConfig, "Area " + type); qCDebug(SHELL) << "Trying to restore area " << type; /* This is just an easy check that a group exists, to avoid "restoring" area from empty config group, wiping away programmatically installed defaults. */ if (areaConfig.readEntry("id", "") == type) { qCDebug(SHELL) << "Restoring area " << type; loadArea(area, areaConfig); } // At this point we know which toolviews the area wants. // Tender all tool views we have. QMap::const_iterator i, e; for (i = d->factoryDocuments.constBegin(), e = d->factoryDocuments.constEnd(); i != e; ++i) { addToolViewIfWanted(i.key(), i.value(), area); } } // Force reload of the changes. showAreaInternal(mw->area(), mw); mw->enableAreaSettingsSave(); } d->areasRestored = true; } void UiController::addToolViewToDockArea(IToolViewFactory* factory, Qt::DockWidgetArea area) { addToolViewToArea(factory, d->factoryDocuments[factory], activeArea(), Sublime::dockAreaToPosition(area)); } bool UiController::toolViewPresent(Sublime::ToolDocument* doc, Sublime::Area* area) { foreach (Sublime::View *view, doc->views()) { if( area->toolViews().contains( view ) ) return true; } return false; } void UiController::addToolViewIfWanted(IToolViewFactory* factory, Sublime::ToolDocument* doc, Sublime::Area* area) { if (area->wantToolView(factory->id())) { addToolViewToArea(factory, doc, area); } } Sublime::View* UiController::addToolViewToArea(IToolViewFactory* factory, Sublime::ToolDocument* doc, Sublime::Area* area, Sublime::Position p) { Sublime::View* view = doc->createView(); area->addToolView( view, p == Sublime::AllPositions ? Sublime::dockAreaToPosition(factory->defaultPosition()) : p); connect(view, &Sublime::View::raise, this, static_cast(&UiController::raiseToolView)); factory->viewCreated(view); return view; } void UiController::registerStatus(QObject* status) { Sublime::MainWindow* w = activeSublimeWindow(); if (!w) return; MainWindow* mw = qobject_cast(w); if (!mw) return; mw->registerStatus(status); } void UiController::showErrorMessage(const QString& message, int timeout) { Sublime::MainWindow* w = activeSublimeWindow(); if (!w) return; MainWindow* mw = qobject_cast(w); if (!mw) return; QMetaObject::invokeMethod(mw, "showErrorMessage", Q_ARG(QString, message), Q_ARG(int, timeout)); } void UiController::hideAssistant() { if (d->currentShownAssistant) { d->currentShownAssistant->hide(); } } void UiController::popUpAssistant(const KDevelop::IAssistant::Ptr& assistant) { if(!assistant) return; Sublime::View* view = d->activeSublimeWindow->activeView(); if( !view ) { qCDebug(SHELL) << "no active view in mainwindow"; return; } auto editorView = qobject_cast(view->widget()); Q_ASSERT(editorView); if (editorView) { if ( !d->currentShownAssistant ) { d->currentShownAssistant = new AssistantPopup; } d->currentShownAssistant->reset(editorView, assistant); } } const QMap< IToolViewFactory*, Sublime::ToolDocument* >& UiController::factoryDocuments() const { return d->factoryDocuments; } +QWidget* UiController::activeToolViewActionListener() const +{ + return d->activeActionListener; +} + } #include "uicontroller.moc" #include "moc_uicontroller.cpp" diff --git a/shell/uicontroller.h b/shell/uicontroller.h index cc904ece89..372562e9d9 100644 --- a/shell/uicontroller.h +++ b/shell/uicontroller.h @@ -1,127 +1,129 @@ /*************************************************************************** * Copyright 2007 Alexander Dymo * * * * 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_UICONTROLLER_H #define KDEVPLATFORM_UICONTROLLER_H #include "shellexport.h" #include #include #include #include class QListWidgetItem; namespace Sublime { class AreaIndex; class ToolDocument; } namespace KDevelop { class Core; class MainWindow; class KDEVPLATFORMSHELL_EXPORT UiController: public Sublime::Controller, public IUiController { Q_OBJECT public: UiController(Core *core); virtual ~UiController(); /** @return area for currently active sublime mainwindow or 0 if no sublime mainwindow is active.*/ virtual Sublime::Area *activeArea(); /** @return active sublime mainwindow or 0 if no such mainwindow is active.*/ virtual Sublime::MainWindow *activeSublimeWindow(); /** @return active sublime mainwindow or 0 if no such mainwindow is active.*/ virtual KParts::MainWindow *activeMainWindow(); /** @return default main window - the main window for default area in the shell. No guarantee is given that it always exists so this method may return 0.*/ MainWindow *defaultMainWindow(); /** @return the default area for this shell.*/ Sublime::Area *defaultArea(); virtual void switchToArea(const QString &areaName, SwitchMode switchMode); virtual void addToolView(const QString &name, IToolViewFactory *factory); virtual void removeToolView(IToolViewFactory *factory); virtual QWidget* findToolView(const QString& name, IToolViewFactory *factory, FindFlags flags); virtual void raiseToolView(QWidget* toolViewWidget); void selectNewToolViewToAdd(MainWindow *mw); void initialize(); void cleanup(); void showSettingsDialog(); Sublime::Controller* controller(); void mainWindowDeleted(MainWindow* mw); void saveAllAreas(KSharedConfigPtr config); void saveArea(Sublime::Area* area, KConfigGroup & group); void loadAllAreas(KSharedConfigPtr config); void loadArea(Sublime::Area* area, const KConfigGroup & group); /*! @p status must implement KDevelop::IStatus */ virtual void registerStatus(QObject* status); virtual void popUpAssistant(const KDevelop::IAssistant::Ptr& assistant); virtual void showErrorMessage(const QString& message, int timeout); /// Returns list of available view factories together with their ToolDocuments. /// @see addToolView(), removeToolView(), findToolView() const QMap& factoryDocuments() const; /// Adds a tool view in the active area to the dock area @p area. /// @see activeArea() void addToolViewToDockArea(KDevelop::IToolViewFactory* factory, Qt::DockWidgetArea area); bool toolViewPresent(Sublime::ToolDocument* doc, Sublime::Area* area); + virtual QWidget* activeToolViewActionListener() const override; + public Q_SLOTS: void raiseToolView(Sublime::View * view); private Q_SLOTS: void addNewToolView(MainWindow* mw, QListWidgetItem* item); void hideAssistant(); private: void addToolViewIfWanted(IToolViewFactory* factory, Sublime::ToolDocument* doc, Sublime::Area* area); Sublime::View* addToolViewToArea(IToolViewFactory* factory, Sublime::ToolDocument* doc, Sublime::Area* area, Sublime::Position p=Sublime::AllPositions); void setupActions(); class UiControllerPrivate* const d; friend class UiControllerPrivate; Q_PRIVATE_SLOT(d, void widgetChanged(QWidget*,QWidget*)) }; } #endif