diff options
author | hjk <hjk121@nokiamail.com> | 2014-01-13 16:17:34 +0100 |
---|---|---|
committer | Eike Ziller <eike.ziller@digia.com> | 2014-01-14 07:43:00 +0100 |
commit | 4d96fa7aba7be35800d61d8bed89d3f6c3ef9329 (patch) | |
tree | c9b102981cf81023e1488224a24758af18aa064e /src/plugins/coreplugin/find/searchresultwidget.cpp | |
parent | 8b854270a6c214479b2cdf302072a3e74fa854da (diff) | |
download | qt-creator-4d96fa7aba7be35800d61d8bed89d3f6c3ef9329.tar.gz |
Core: Merge Find and Locator into Core plugin
Change-Id: I7053310272235d854c9f409670ff52a10a7add8b
Reviewed-by: Christian Kandeler <christian.kandeler@digia.com>
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
Reviewed-by: Eike Ziller <eike.ziller@digia.com>
Diffstat (limited to 'src/plugins/coreplugin/find/searchresultwidget.cpp')
-rw-r--r-- | src/plugins/coreplugin/find/searchresultwidget.cpp | 495 |
1 files changed, 495 insertions, 0 deletions
diff --git a/src/plugins/coreplugin/find/searchresultwidget.cpp b/src/plugins/coreplugin/find/searchresultwidget.cpp new file mode 100644 index 0000000000..6539cebdad --- /dev/null +++ b/src/plugins/coreplugin/find/searchresultwidget.cpp @@ -0,0 +1,495 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "searchresultwidget.h" +#include "searchresulttreeview.h" +#include "searchresulttreemodel.h" +#include "searchresulttreeitems.h" +#include "searchresulttreeitemroles.h" + +#include "findplugin.h" +#include "treeviewfind.h" + +#include <aggregation/aggregate.h> +#include <coreplugin/coreplugin.h> + +#include <QDir> +#include <QFrame> +#include <QLabel> +#include <QLineEdit> +#include <QToolButton> +#include <QCheckBox> +#include <QVBoxLayout> +#include <QHBoxLayout> + +static const int SEARCHRESULT_WARNING_LIMIT = 200000; +static const char SIZE_WARNING_ID[] = "sizeWarningLabel"; + +namespace Core { +namespace Internal { + +class WideEnoughLineEdit : public QLineEdit { + Q_OBJECT +public: + WideEnoughLineEdit(QWidget *parent):QLineEdit(parent){ + connect(this, SIGNAL(textChanged(QString)), + this, SLOT(updateGeometry())); + } + ~WideEnoughLineEdit(){} + QSize sizeHint() const { + QSize sh = QLineEdit::minimumSizeHint(); + sh.rwidth() += qMax(25 * fontMetrics().width(QLatin1Char('x')), + fontMetrics().width(text())); + return sh; + } +public slots: + void updateGeometry() { QLineEdit::updateGeometry(); } +}; + +} // namespace Internal +} // namespace Core + +using namespace Core; +using namespace Core::Internal; + +SearchResultWidget::SearchResultWidget(QWidget *parent) : + QWidget(parent), + m_count(0), + m_isShowingReplaceUI(false), + m_searchAgainSupported(false) +{ + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(0); + setLayout(layout); + + QFrame *topWidget = new QFrame; + QPalette pal = topWidget->palette(); + pal.setColor(QPalette::Window, QColor(255, 255, 225)); + pal.setColor(QPalette::WindowText, Qt::black); + topWidget->setPalette(pal); + topWidget->setFrameStyle(QFrame::Panel | QFrame::Raised); + topWidget->setLineWidth(1); + topWidget->setAutoFillBackground(true); + QHBoxLayout *topLayout = new QHBoxLayout(topWidget); + topLayout->setMargin(2); + topWidget->setLayout(topLayout); + layout->addWidget(topWidget); + + m_messageWidget = new QFrame; + pal.setColor(QPalette::Window, QColor(255, 255, 225)); + pal.setColor(QPalette::WindowText, Qt::red); + m_messageWidget->setPalette(pal); + m_messageWidget->setFrameStyle(QFrame::Panel | QFrame::Raised); + m_messageWidget->setLineWidth(1); + m_messageWidget->setAutoFillBackground(true); + QHBoxLayout *messageLayout = new QHBoxLayout(m_messageWidget); + messageLayout->setMargin(2); + m_messageWidget->setLayout(messageLayout); + QLabel *messageLabel = new QLabel(tr("Search was canceled.")); + messageLabel->setPalette(pal); + messageLayout->addWidget(messageLabel); + layout->addWidget(m_messageWidget); + m_messageWidget->setVisible(false); + + m_searchResultTreeView = new Internal::SearchResultTreeView(this); + m_searchResultTreeView->setFrameStyle(QFrame::NoFrame); + m_searchResultTreeView->setAttribute(Qt::WA_MacShowFocusRect, false); + Aggregation::Aggregate * agg = new Aggregation::Aggregate; + agg->add(m_searchResultTreeView); + agg->add(new TreeViewFind(m_searchResultTreeView, + ItemDataRoles::ResultLineRole)); + layout->addWidget(m_searchResultTreeView); + + m_infoBarDisplay.setTarget(layout, 2); + m_infoBarDisplay.setInfoBar(&m_infoBar); + + m_descriptionContainer = new QWidget(topWidget); + QHBoxLayout *descriptionLayout = new QHBoxLayout(m_descriptionContainer); + m_descriptionContainer->setLayout(descriptionLayout); + descriptionLayout->setMargin(0); + m_descriptionContainer->setMinimumWidth(200); + m_descriptionContainer->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + m_label = new QLabel(m_descriptionContainer); + m_label->setVisible(false); + m_searchTerm = new QLabel(m_descriptionContainer); + m_searchTerm->setVisible(false); + descriptionLayout->addWidget(m_label); + descriptionLayout->addWidget(m_searchTerm); + m_cancelButton = new QToolButton(topWidget); + m_cancelButton->setText(tr("Cancel")); + m_cancelButton->setToolButtonStyle(Qt::ToolButtonTextOnly); + connect(m_cancelButton, SIGNAL(clicked()), this, SLOT(cancel())); + m_searchAgainButton = new QToolButton(topWidget); + m_searchAgainButton->setToolTip(tr("Repeat the search with same parameters")); + m_searchAgainButton->setText(tr("Search again")); + m_searchAgainButton->setToolButtonStyle(Qt::ToolButtonTextOnly); + m_searchAgainButton->setVisible(false); + connect(m_searchAgainButton, SIGNAL(clicked()), this, SLOT(searchAgain())); + + m_replaceLabel = new QLabel(tr("Replace with:"), topWidget); + m_replaceTextEdit = new WideEnoughLineEdit(topWidget); + m_replaceTextEdit->setMinimumWidth(120); + m_replaceTextEdit->setEnabled(false); + m_replaceTextEdit->setTabOrder(m_replaceTextEdit, m_searchResultTreeView); + m_replaceButton = new QToolButton(topWidget); + m_replaceButton->setToolTip(tr("Replace all occurrences")); + m_replaceButton->setText(tr("Replace")); + m_replaceButton->setToolButtonStyle(Qt::ToolButtonTextOnly); + m_replaceButton->setEnabled(false); + m_preserveCaseCheck = new QCheckBox(topWidget); + m_preserveCaseCheck->setText(tr("Preserve case")); + m_preserveCaseCheck->setEnabled(false); + + if (FindPlugin * plugin = FindPlugin::instance()) { + m_preserveCaseCheck->setChecked(plugin->hasFindFlag(FindPreserveCase)); + connect(m_preserveCaseCheck, SIGNAL(clicked(bool)), plugin, SLOT(setPreserveCase(bool))); + } + + m_matchesFoundLabel = new QLabel(topWidget); + updateMatchesFoundLabel(); + + topLayout->addWidget(m_descriptionContainer); + topLayout->addWidget(m_cancelButton); + topLayout->addWidget(m_searchAgainButton); + topLayout->addWidget(m_replaceLabel); + topLayout->addWidget(m_replaceTextEdit); + topLayout->addWidget(m_replaceButton); + topLayout->addWidget(m_preserveCaseCheck); + topLayout->addStretch(2); + topLayout->addWidget(m_matchesFoundLabel); + topWidget->setMinimumHeight(m_cancelButton->sizeHint().height() + + topLayout->contentsMargins().top() + topLayout->contentsMargins().bottom() + + topWidget->lineWidth()); + setShowReplaceUI(false); + + connect(m_searchResultTreeView, SIGNAL(jumpToSearchResult(SearchResultItem)), + this, SLOT(handleJumpToSearchResult(SearchResultItem))); + connect(m_replaceTextEdit, SIGNAL(returnPressed()), this, SLOT(handleReplaceButton())); + connect(m_replaceButton, SIGNAL(clicked()), this, SLOT(handleReplaceButton())); +} + +SearchResultWidget::~SearchResultWidget() +{ + if (m_infoBar.containsInfo(Core::Id(SIZE_WARNING_ID))) + cancelAfterSizeWarning(); +} + +void SearchResultWidget::setInfo(const QString &label, const QString &toolTip, const QString &term) +{ + m_label->setText(label); + m_label->setVisible(!label.isEmpty()); + m_descriptionContainer->setToolTip(toolTip); + m_searchTerm->setText(term); + m_searchTerm->setVisible(!term.isEmpty()); +} + +void SearchResultWidget::addResult(const QString &fileName, int lineNumber, const QString &rowText, + int searchTermStart, int searchTermLength, const QVariant &userData) +{ + SearchResultItem item; + item.path = QStringList() << QDir::toNativeSeparators(fileName); + item.lineNumber = lineNumber; + item.text = rowText; + item.textMarkPos = searchTermStart; + item.textMarkLength = searchTermLength; + item.useTextEditorFont = true; + item.userData = userData; + addResults(QList<SearchResultItem>() << item, SearchResult::AddOrdered); +} + +void SearchResultWidget::addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode) +{ + bool firstItems = (m_count == 0); + m_count += items.size(); + m_searchResultTreeView->addResults(items, mode); + updateMatchesFoundLabel(); + if (firstItems) { + if (!m_dontAskAgainGroup.isEmpty()) { + Core::Id undoWarningId = Core::Id("warninglabel/").withSuffix(m_dontAskAgainGroup); + if (m_infoBar.canInfoBeAdded(undoWarningId)) { + Core::InfoBarEntry info(undoWarningId, tr("This change cannot be undone."), + Core::InfoBarEntry::GlobalSuppressionEnabled); + m_infoBar.addInfo(info); + } + } + + m_replaceTextEdit->setEnabled(true); + // We didn't have an item before, set the focus to the search widget or replace text edit + if (m_isShowingReplaceUI) { + m_replaceTextEdit->setFocus(); + m_replaceTextEdit->selectAll(); + } else { + m_searchResultTreeView->setFocus(); + } + m_searchResultTreeView->selectionModel()->select(m_searchResultTreeView->model()->index(0, 0, QModelIndex()), QItemSelectionModel::Select); + emit navigateStateChanged(); + } else if (m_count <= SEARCHRESULT_WARNING_LIMIT) { + return; + } else { + Core::Id sizeWarningId(SIZE_WARNING_ID); + if (!m_infoBar.canInfoBeAdded(sizeWarningId)) + return; + emit paused(true); + Core::InfoBarEntry info(sizeWarningId, + tr("The search resulted in more than %n items, do you still want to continue?", + 0, SEARCHRESULT_WARNING_LIMIT)); + info.setCancelButtonInfo(tr("Cancel"), this, SLOT(cancelAfterSizeWarning())); + info.setCustomButtonInfo(tr("Continue"), this, SLOT(continueAfterSizeWarning())); + m_infoBar.addInfo(info); + emit requestPopup(false/*no focus*/); + } +} + + + +int SearchResultWidget::count() const +{ + return m_count; +} + +QString SearchResultWidget::dontAskAgainGroup() const +{ + return m_dontAskAgainGroup; +} + +void SearchResultWidget::setDontAskAgainGroup(const QString &group) +{ + m_dontAskAgainGroup = group; +} + + +void SearchResultWidget::setTextToReplace(const QString &textToReplace) +{ + m_replaceTextEdit->setText(textToReplace); +} + +QString SearchResultWidget::textToReplace() const +{ + return m_replaceTextEdit->text(); +} + +void SearchResultWidget::setShowReplaceUI(bool visible) +{ + m_searchResultTreeView->model()->setShowReplaceUI(visible); + m_replaceLabel->setVisible(visible); + m_replaceTextEdit->setVisible(visible); + m_replaceButton->setVisible(visible); + m_preserveCaseCheck->setVisible(visible); + m_isShowingReplaceUI = visible; +} + +bool SearchResultWidget::hasFocusInternally() const +{ + return m_searchResultTreeView->hasFocus() || (m_isShowingReplaceUI && m_replaceTextEdit->hasFocus()); +} + +void SearchResultWidget::setFocusInternally() +{ + if (m_count > 0) { + if (m_isShowingReplaceUI) { + if (!focusWidget() || focusWidget() == m_replaceTextEdit) { + m_replaceTextEdit->setFocus(); + m_replaceTextEdit->selectAll(); + } else { + m_searchResultTreeView->setFocus(); + } + } else { + m_searchResultTreeView->setFocus(); + } + } +} + +bool SearchResultWidget::canFocusInternally() const +{ + return m_count > 0; +} + +void SearchResultWidget::notifyVisibilityChanged(bool visible) +{ + emit visibilityChanged(visible); +} + +void SearchResultWidget::setTextEditorFont(const QFont &font, const SearchResultColor color) +{ + m_searchResultTreeView->setTextEditorFont(font, color); +} + +void SearchResultWidget::setAutoExpandResults(bool expand) +{ + m_searchResultTreeView->setAutoExpandResults(expand); +} + +void SearchResultWidget::expandAll() +{ + m_searchResultTreeView->expandAll(); +} + +void SearchResultWidget::collapseAll() +{ + m_searchResultTreeView->collapseAll(); +} + +void SearchResultWidget::goToNext() +{ + if (m_count == 0) + return; + QModelIndex idx = m_searchResultTreeView->model()->next(m_searchResultTreeView->currentIndex()); + if (idx.isValid()) { + m_searchResultTreeView->setCurrentIndex(idx); + m_searchResultTreeView->emitJumpToSearchResult(idx); + } +} + +void SearchResultWidget::goToPrevious() +{ + if (!m_searchResultTreeView->model()->rowCount()) + return; + QModelIndex idx = m_searchResultTreeView->model()->prev(m_searchResultTreeView->currentIndex()); + if (idx.isValid()) { + m_searchResultTreeView->setCurrentIndex(idx); + m_searchResultTreeView->emitJumpToSearchResult(idx); + } +} + +void SearchResultWidget::restart() +{ + m_replaceTextEdit->setEnabled(false); + m_replaceButton->setEnabled(false); + m_searchResultTreeView->clear(); + m_count = 0; + Core::Id sizeWarningId(SIZE_WARNING_ID); + m_infoBar.removeInfo(sizeWarningId); + m_infoBar.enableInfo(sizeWarningId); + m_cancelButton->setVisible(true); + m_searchAgainButton->setVisible(false); + m_messageWidget->setVisible(false); + updateMatchesFoundLabel(); + emit restarted(); +} + +void SearchResultWidget::setSearchAgainSupported(bool supported) +{ + m_searchAgainSupported = supported; + m_searchAgainButton->setVisible(supported && !m_cancelButton->isVisible()); +} + +void SearchResultWidget::setSearchAgainEnabled(bool enabled) +{ + m_searchAgainButton->setEnabled(enabled); +} + +void SearchResultWidget::finishSearch(bool canceled) +{ + Core::Id sizeWarningId(SIZE_WARNING_ID); + m_infoBar.removeInfo(sizeWarningId); + m_infoBar.enableInfo(sizeWarningId); + m_replaceTextEdit->setEnabled(m_count > 0); + m_replaceButton->setEnabled(m_count > 0); + m_preserveCaseCheck->setEnabled(m_count > 0); + m_cancelButton->setVisible(false); + m_messageWidget->setVisible(canceled); + m_searchAgainButton->setVisible(m_searchAgainSupported); +} + +void SearchResultWidget::sendRequestPopup() +{ + emit requestPopup(true/*focus*/); +} + +void SearchResultWidget::continueAfterSizeWarning() +{ + m_infoBar.suppressInfo(Core::Id(SIZE_WARNING_ID)); + emit paused(false); +} + +void SearchResultWidget::cancelAfterSizeWarning() +{ + m_infoBar.suppressInfo(Core::Id(SIZE_WARNING_ID)); + emit cancelled(); + emit paused(false); +} + +void SearchResultWidget::handleJumpToSearchResult(const SearchResultItem &item) +{ + emit activated(item); +} + +void SearchResultWidget::handleReplaceButton() +{ + // check if button is actually enabled, because this is also triggered + // by pressing return in replace line edit + if (m_replaceButton->isEnabled()) { + m_infoBar.clear(); + emit replaceButtonClicked(m_replaceTextEdit->text(), checkedItems(), m_preserveCaseCheck->isChecked()); + } +} + +void SearchResultWidget::cancel() +{ + m_cancelButton->setVisible(false); + if (m_infoBar.containsInfo(Core::Id(SIZE_WARNING_ID))) + cancelAfterSizeWarning(); + else + emit cancelled(); +} + +void SearchResultWidget::searchAgain() +{ + emit searchAgainRequested(); +} + +QList<SearchResultItem> SearchResultWidget::checkedItems() const +{ + QList<SearchResultItem> result; + Internal::SearchResultTreeModel *model = m_searchResultTreeView->model(); + const int fileCount = model->rowCount(QModelIndex()); + for (int i = 0; i < fileCount; ++i) { + QModelIndex fileIndex = model->index(i, 0, QModelIndex()); + Internal::SearchResultTreeItem *fileItem = static_cast<Internal::SearchResultTreeItem *>(fileIndex.internalPointer()); + Q_ASSERT(fileItem != 0); + for (int rowIndex = 0; rowIndex < fileItem->childrenCount(); ++rowIndex) { + QModelIndex textIndex = model->index(rowIndex, 0, fileIndex); + Internal::SearchResultTreeItem *rowItem = static_cast<Internal::SearchResultTreeItem *>(textIndex.internalPointer()); + if (rowItem->checkState()) + result << rowItem->item; + } + } + return result; +} + +void SearchResultWidget::updateMatchesFoundLabel() +{ + if (m_count == 0) + m_matchesFoundLabel->setText(tr("No matches found.")); + else + m_matchesFoundLabel->setText(tr("%n matches found.", 0, m_count)); +} + +#include "searchresultwidget.moc" |