diff --git a/plugins/grepview/grepoutputview.cpp b/plugins/grepview/grepoutputview.cpp index 9b3522f8bc..8552ffa71b 100644 --- a/plugins/grepview/grepoutputview.cpp +++ b/plugins/grepview/grepoutputview.cpp @@ -1,389 +1,390 @@ /************************************************************************** * 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, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(modelSelectorContextMenu(QPoint))); connect(modelSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(changeModel(int))); resultsTreeView->setItemDelegate(GrepOutputDelegate::self()); resultsTreeView->setHeaderHidden(true); resultsTreeView->setUniformRowHeights(false); resultsTreeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); connect(m_prev, SIGNAL(triggered(bool)), this, SLOT(selectPreviousItem())); connect(m_next, SIGNAL(triggered(bool)), this, SLOT(selectNextItem())); connect(m_collapseAll, SIGNAL(triggered(bool)), this, SLOT(collapseAllItems())); connect(m_expandAll, SIGNAL(triggered(bool)), this, SLOT(expandAllItems())); connect(applyButton, SIGNAL(clicked()), this, SLOT(onApply())); connect(m_clearSearchHistory, SIGNAL(triggered(bool)), this, SLOT(clearSearchHistory())); connect(resultsTreeView, SIGNAL(collapsed(QModelIndex)), this, SLOT(updateScrollArea(QModelIndex))); connect(resultsTreeView, SIGNAL(expanded(QModelIndex)), this, SLOT(updateScrollArea(QModelIndex))); IPlugin *outputView = ICore::self()->pluginController()->pluginForExtension("org.kdevelop.IOutputView"); 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, SIGNAL(editTextChanged(QString)), SLOT(replacementTextChanged(QString))); connect(newSearchAction, SIGNAL(triggered(bool)), this, SLOT(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, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(rowsRemoved())); connect(resultsTreeView, SIGNAL(activated(QModelIndex)), newModel, SLOT(activate(QModelIndex))); connect(replacementCombo, SIGNAL(editTextChanged(QString)), newModel, SLOT(setReplacement(QString))); connect(newModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(expandElements(QModelIndex))); connect(newModel, SIGNAL(showErrorMessage(QString,int)), this, SLOT(showErrorMessage(QString))); QString prettyUrl = descriptionOrUrl; if(descriptionOrUrl.startsWith('/')) prettyUrl = ICore::self()->projectController()->prettyFileName(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(), SIGNAL(showMessage(KDevelop::IStatus*,QString)), this, SLOT(showMessage(KDevelop::IStatus*,QString))); disconnect(model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(updateApplyState(QModelIndex,QModelIndex))); } 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(), SIGNAL(showMessage(KDevelop::IStatus*,QString)), this, SLOT(showMessage(KDevelop::IStatus*,QString))); connect(model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(updateApplyState(QModelIndex,QModelIndex))); 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/shell/assistantpopup.cpp b/shell/assistantpopup.cpp index 95bd979942..6e5ddc2b04 100644 --- a/shell/assistantpopup.cpp +++ b/shell/assistantpopup.cpp @@ -1,248 +1,250 @@ /* Copyright 2009 David Nolden Copyright 2012 Milian Wolff Copyright 2014 Sven Brauch This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "assistantpopup.h" #include "sublime/holdupdates.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; AssistantPopup::AssistantPopup(KTextEditor::View* parent, const IAssistant::Ptr& assistant) : m_assistant(assistant) , m_view(parent) , m_shownAtBottom(false) { #if 0 // TODO KF5: Port me QPalette p = palette(); p.setColor(QPalette::Window, Qt::transparent); setPalette(p); setBackgroundRole(QPalette::Window); setBackgroundBrush(QBrush(QColor(0, 0, 0, 0))); #endif setResizeMode(QQuickView::SizeViewToRootObject); m_config = new AssistantPopupConfig(this); auto view = ICore::self()->documentController()->activeTextDocumentView(); m_config->setColorsFromView(view); updateActions(); rootContext()->setContextProperty("config", QVariant::fromValue(m_config)); setSource(QUrl(KStandardDirs::locate("data", "kdevelop/assistantpopup.qml"))); Q_ASSERT(assistant); if ( ! rootObject() ) { kWarning() << "Failed to load assistant markup! The assistant will not work."; return; } connect(m_view, SIGNAL(verticalScrollPositionChanged(KTextEditor::View*,KTextEditor::Cursor)), this, SLOT(updatePosition(KTextEditor::View*,KTextEditor::Cursor))); updatePosition(m_view, KTextEditor::Cursor::invalid()); connect(m_view, SIGNAL(destroyed(QObject*)), this, SLOT(deleteLater())); m_view->installEventFilter(this); } +#if 0 bool AssistantPopup::viewportEvent(QEvent *event) { // For some reason, QGraphicsView posts a WindowActivate event // when it is shown, even if disabled through setting the WA_ShowWithoutActivate // attribute. This causes all focus-driven popups (QuickOpen, tooltips, ...) // to hide when the assistant opens. Thus, prevent it from processing the Show event here. if ( event->type() == QEvent::Show ) { return true; } return QGraphicsView::viewportEvent(event); } +#endif AssistantPopupConfig::AssistantPopupConfig(QObject *parent): QObject(parent) { } void AssistantPopupConfig::setColorsFromView(QObject *view) { auto iface = dynamic_cast(view); Q_ASSERT(iface); m_foreground = iface->configValue("line-number-color").value(); m_background = iface->configValue("icon-border-color").value(); m_highlight = iface->configValue("folding-marker-color").value(); if ( KColorUtils::luma(m_background) < 0.3 ) { m_foreground = KColorUtils::lighten(m_foreground, 0.7); } const float lumaDiff = KColorUtils::luma(m_highlight) - KColorUtils::luma(m_background); if ( fabs(lumaDiff) < 0.5 ) { m_highlight = QColor::fromHsv(m_highlight.hue(), qMin(255, m_highlight.saturation() + 80), lumaDiff > 0 ? qMin(255, m_highlight.value() + 120) : qMax(80, m_highlight.value() - 40)); } } static QWidget* findByClassname(KTextEditor::View* view, const QString& klass) { auto children = view->findChildren(); for ( auto child: children ) { if ( child->metaObject()->className() == klass ) { return child; } } return static_cast(nullptr); }; QRect AssistantPopup::textWidgetGeometry(KTextEditor::View *view) const { // Subtract the width of the right scrollbar int scrollbarWidth = 0; if ( auto scrollbar = findByClassname(view, "KateScrollBar") ) { scrollbarWidth = scrollbar->width(); } // Subtract the width of the bottom scrollbar int bottomScrollbarWidth = 0; if ( auto bottom = findByClassname(view, "QScrollBar") ) { bottomScrollbarWidth = bottom->height(); } auto geom = view->geometry(); geom.adjust(0, 0, -scrollbarWidth, -bottomScrollbarWidth); return geom; } void AssistantPopup::keyReleaseEvent(QKeyEvent *event) { if ( event->key() == Qt::Key_Alt ) { m_view->setFocus(); emit m_config->shouldShowHighlight(false); } QQuickView::keyReleaseEvent(event); } bool AssistantPopup::eventFilter(QObject* object, QEvent* event) { Q_ASSERT(object == m_view); Q_UNUSED(object); if (event->type() == QEvent::Resize) { updatePosition(m_view, KTextEditor::Cursor::invalid()); } else if (event->type() == QEvent::Hide) { executeHideAction(); } else if (event->type() == QEvent::KeyPress) { // While the Alt key is pressed, give focus to the assistant widget // and notify it about that. auto modifiers = static_cast(event)->modifiers(); if (modifiers == Qt::AltModifier) { #if 0 // TODO KF5 Needed? setFocus(); #endif emit m_config->shouldShowHighlight(true); return true; } if (static_cast(event)->key() == Qt::Key_Escape) { executeHideAction(); } } return false; } void AssistantPopup::updatePosition(KTextEditor::View* view, const KTextEditor::Cursor& newPos) { if ( newPos.isValid() && newPos.line() != 0 && ! m_shownAtBottom ) { // the position is not going to change; don't waste time return; } auto editorGeometry = textWidgetGeometry(view); auto cursor = view->cursorToCoordinate(KTextEditor::Cursor(0, 0)); const int margin = 12; #if 0 // TODO KF5: Port Sublime::HoldUpdates hold(ICore::self()->uiController()->activeMainWindow()); QPoint targetLocation; if ( cursor.y() < 0 ) { // Only when the view is not scrolled to the top, place the widget there; otherwise it easily gets // in the way. targetLocation = parentWidget()->mapFromGlobal(view->mapToGlobal(editorGeometry.topRight() + QPoint(-width() - margin, margin))); m_shownAtBottom = false; } else { targetLocation = parentWidget()->mapFromGlobal(view->mapToGlobal(editorGeometry.bottomRight() + QPoint(-width() - margin, -margin - height()))); m_shownAtBottom = true; } if ( pos() != targetLocation ) { move(targetLocation); } #endif } IAssistant::Ptr AssistantPopup::assistant() const { return m_assistant; } void AssistantPopup::executeHideAction() { if ( isVisible() ) { m_assistant->doHide(); m_view->setFocus(); } } void AssistantPopup::notifyReopened() { emit m_config->shouldCancelAnimation(); } void AssistantPopup::updateActions() { m_assistantActions = m_assistant->actions(); QList items; foreach(IAssistantAction::Ptr action, m_assistantActions) { items << new AssistantButton(action->toKAction(), action->description(), this); } auto hideAction = new QAction(i18n("Hide"), this); connect(hideAction, SIGNAL(triggered()), this, SLOT(executeHideAction())); items << new AssistantButton(hideAction, hideAction->text(), this); m_config->setModel(items); m_config->setTitle(m_assistant->title()); } #include "assistantpopup.moc" diff --git a/shell/assistantpopup.h b/shell/assistantpopup.h index 7775a93c20..e4a5621cfc 100644 --- a/shell/assistantpopup.h +++ b/shell/assistantpopup.h @@ -1,132 +1,134 @@ /* Copyright 2009 David Nolden Copyright 2012 Milian Wolff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_ASSISTANTPOPUP_H #define KDEVPLATFORM_ASSISTANTPOPUP_H #include #include #include #include #include namespace KTextEditor { class View; class Cursor; } class AssistantButton : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name CONSTANT) public: AssistantButton(QAction* action, const QString& text, QObject* parent) : QObject(parent) , m_name(text) , m_action(action) { } QString name() const { return m_name; } Q_INVOKABLE void trigger() { m_action->trigger(); } private: QString m_name; QAction* m_action; }; class AssistantPopupConfig : public QObject { Q_OBJECT Q_PROPERTY(QColor foreground READ foreground CONSTANT) Q_PROPERTY(QColor background READ background CONSTANT) Q_PROPERTY(QColor highlight READ highlight CONSTANT) Q_PROPERTY(QString title READ title CONSTANT) Q_PROPERTY(QList model READ model CONSTANT) public: explicit AssistantPopupConfig(QObject *parent = 0); QColor foreground() const { return m_foreground; } QColor background() const { return m_background; } QColor highlight() const { return m_highlight; } QString title() const { return m_title; } QList model() const { return m_model; } void setTitle(const QString& text) { m_title = text; } void setModel(const QList& model) { m_model = model; } void setColorsFromView(QObject *view); signals: void shouldShowHighlight(bool show); void shouldCancelAnimation(); private: QColor m_foreground; QColor m_background; QColor m_highlight; QString m_title; QList m_model; friend class AssistantPopup; }; Q_DECLARE_METATYPE(AssistantPopupConfig*) class AssistantPopup : public QQuickView { Q_OBJECT public: typedef KSharedPtr Ptr; /** * @p widget The widget below which the assistant should be shown. * The current main window will be used as parent widget for the popup. * This is to make use of the maximal space available and prevent any lines * in e.g. the editor to be hidden by the popup. */ AssistantPopup(KTextEditor::View* widget, const KDevelop::IAssistant::Ptr& assistant); KDevelop::IAssistant::Ptr assistant() const; +#if 0 virtual bool viewportEvent(QEvent *event); +#endif public slots: void executeHideAction(); void notifyReopened(); private slots: void updatePosition(KTextEditor::View* view, const KTextEditor::Cursor& newPos); private: virtual bool eventFilter(QObject* object, QEvent* event); virtual void keyReleaseEvent(QKeyEvent* event); /** * @brief Get the geometry of the inner part (with the text) of the KTextEditor::View being used. */ QRect textWidgetGeometry(KTextEditor::View *view) const; void updateActions(); QWidget* widgetForAction(const KDevelop::IAssistantAction::Ptr& action, int& mnemonic); KDevelop::IAssistant::Ptr m_assistant; QList m_assistantActions; KTextEditor::View* m_view; AssistantPopupConfig* m_config; bool m_shownAtBottom; }; #endif // KDEVPLATFORM_ASSISTANTPOPUP_H