SVN_SILENT made messages (.desktop file) - always resolve ours
[trojita.git] / src / Gui / FindBar.cpp
bloba7c65d3fa2050a59bc6f18c1195120db41072ee8
1 /* ============================================================
3 * This file is a part of the rekonq project
5 * Copyright (C) 2008-2012 by Andrea Diamantini <adjam7 at gmail dot com>
6 * Copyright (C) 2009-2011 by Lionel Chauvin <megabigbug@yahoo.fr>
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License or (at your option) version 3 or any later version
13 * accepted by the membership of KDE e.V. (or its successor approved
14 * by the membership of KDE e.V.), which shall act as a proxy
15 * defined in Section 14 of version 3 of the license.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 * ============================================================ */
28 #include "FindBar.h"
29 #include <QAbstractScrollArea>
30 #include <QCheckBox>
31 #include <QHBoxLayout>
32 #include <QKeyEvent>
33 #include <QLabel>
34 #include <QPushButton>
35 #include <QScrollBar>
36 #include <QToolButton>
37 #include <QWebFrame>
38 #include <QWebView>
39 #include "LineEdit.h"
40 #include "Gui/EmbeddedWebView.h"
41 #include "UiUtils/Color.h"
42 #include "UiUtils/IconLoader.h"
44 namespace Gui {
46 FindBar::FindBar(QWidget *parent)
47 : QWidget(parent)
48 , m_lineEdit(new LineEdit(this))
49 , m_matchCase(new QCheckBox(tr("&Match case"), this))
50 , m_highlightAll(new QCheckBox(tr("&Highlight all"), this)),
51 m_associatedWebView(nullptr)
53 QHBoxLayout *layout = new QHBoxLayout;
55 // cosmetic
56 layout->setContentsMargins(2, 0, 2, 0);
58 // hide button
59 QToolButton *hideButton = new QToolButton(this);
60 hideButton->setAutoRaise(true);
61 hideButton->setIcon(UiUtils::loadIcon(QStringLiteral("dialog-close")));
62 hideButton->setShortcut(tr("Esc"));
63 connect(hideButton, &QAbstractButton::clicked, this, &QWidget::hide);
64 layout->addWidget(hideButton);
65 layout->setAlignment(hideButton, Qt::AlignLeft | Qt::AlignTop);
67 // label
68 QLabel *label = new QLabel(tr("Find:"));
69 layout->addWidget(label);
71 // Find Bar signal
72 connect(this, &FindBar::searchString, this, &FindBar::findText);
74 // lineEdit, focusProxy
75 setFocusProxy(m_lineEdit);
76 m_lineEdit->setMaximumWidth(250);
77 connect(m_lineEdit, &QLineEdit::textChanged, this, &FindBar::findText);
78 layout->addWidget(m_lineEdit);
80 // buttons
81 QPushButton *findNext = new QPushButton(UiUtils::loadIcon(QStringLiteral("go-down")), tr("&Next"), this);
82 findNext->setShortcut(tr("F3"));
83 QPushButton *findPrev = new QPushButton(UiUtils::loadIcon(QStringLiteral("go-up")), tr("&Previous"), this);
84 //: Translators: You can change this shortcut, but button names like Shift should not be localized here.
85 //: That will break setting the shortcut. Button names will still appear localized in the UI.
86 findPrev->setShortcut(tr("Shift+F3"));
87 connect(findNext, &QAbstractButton::clicked, this, &FindBar::findNext);
88 connect(findPrev, &QAbstractButton::clicked, this, &FindBar::findPrevious);
89 layout->addWidget(findNext);
90 layout->addWidget(findPrev);
92 // Case sensitivity. Deliberately set so this is off by default.
93 m_matchCase->setCheckState(Qt::Unchecked);
94 m_matchCase->setTristate(false);
95 connect(m_matchCase, &QAbstractButton::toggled, this, &FindBar::matchCaseUpdate);
96 layout->addWidget(m_matchCase);
98 // Hightlight All. On by default
99 m_highlightAll->setCheckState(Qt::Checked);
100 m_highlightAll->setTristate(false);
101 connect(m_highlightAll, &QAbstractButton::toggled, this, &FindBar::updateHighlight);
102 layout->addWidget(m_highlightAll);
104 // stretching widget on the left
105 layout->addStretch();
107 setLayout(layout);
109 // we start off hidden
110 hide();
114 void FindBar::keyPressEvent(QKeyEvent *event)
116 if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) {
117 if (event->modifiers() == Qt::ShiftModifier) {
118 findPrevious();
119 } else {
120 findNext();
124 QWidget::keyPressEvent(event);
128 bool FindBar::matchCase() const
130 return m_matchCase->isChecked();
134 bool FindBar::highlightAllState() const
136 return m_highlightAll->isChecked();
139 void FindBar::resetAssociatedWebView()
141 m_associatedWebView = nullptr;
142 m_lineEdit->clear();
143 QWidget::hide();
146 void FindBar::setVisible(bool visible)
148 QWidget::setVisible(visible);
150 if (!m_associatedWebView)
151 return;
153 if (visible) {
154 const QString selectedText = m_associatedWebView->page()->selectedText();
155 if (!hasFocus() && !selectedText.isEmpty()) {
156 const QString previousText = m_lineEdit->text();
157 m_lineEdit->setText(selectedText);
159 if (m_lineEdit->text() != previousText) {
160 findPrevious();
161 } else {
162 updateHighlight();
164 } else if (selectedText.isEmpty()) {
165 emit searchString(m_lineEdit->text());
168 m_lineEdit->setFocus();
169 m_lineEdit->selectAll();
170 } else {
171 updateHighlight();
173 // Clear the selection
174 m_associatedWebView->page()->findText(QString());
179 void FindBar::notifyMatch(bool match)
181 if (m_lineEdit->text().isEmpty()) {
182 m_lineEdit->setPalette(QPalette());
183 } else {
184 QColor backgroundTint = match ? QColor(0, 0xff, 0, 0x20) : QColor(0xff, 0, 0, 0x20);
185 QPalette p;
186 p.setColor(QPalette::Base, UiUtils::tintColor(p.color(QPalette::Base), backgroundTint));
187 m_lineEdit->setPalette(p);
191 void FindBar::findText(const QString &search)
193 if (!m_associatedWebView)
194 return;
195 _lastStringSearched = search;
196 updateHighlight();
197 findNext();
201 void FindBar::find(FindBar::FindDirection dir)
203 Q_ASSERT(m_associatedWebView);
205 if (isHidden()) {
206 QPoint previous_position = m_associatedWebView->page()->currentFrame()->scrollPosition();
207 m_associatedWebView->page()->focusNextPrevChild(true);
208 m_associatedWebView->page()->currentFrame()->setScrollPosition(previous_position);
209 return;
212 QWebPage::FindFlags options = QWebPage::FindWrapsAroundDocument;
213 if (dir == Backward)
214 options |= QWebPage::FindBackward;
215 if (matchCase())
216 options |= QWebPage::FindCaseSensitively;
218 // HACK Because we're using the QWebView inside a QScrollArea container, the attempts
219 // to scroll the QWebView itself have no direct effect.
220 // Therefore we temporarily shrink the page viewport to the message viewport (ie. the size it
221 // can cover at max), then perform the search, store the scrollPosition, restore the page viewport
222 // and finally scroll the messageview to the gathered scrollPosition, mapped to the message (ie.
223 // usually offset by the mail header)
225 auto emb = qobject_cast<EmbeddedWebView *>(m_associatedWebView);
227 QAbstractScrollArea *container = emb ? static_cast<QAbstractScrollArea*>(emb->scrollParent()) : nullptr;
228 const QSize oldVpS = m_associatedWebView->page()->viewportSize();
229 const bool useResizeTrick = container ? !!container->verticalScrollBar() : false;
230 // first shrink the page viewport
231 if (useResizeTrick) {
232 m_associatedWebView->setUpdatesEnabled(false); // don't let the user see the flicker we might produce
233 QSize newVpS = oldVpS.boundedTo(container->size());
234 m_associatedWebView->page()->setViewportSize(newVpS);
237 // now perform the search (pot. in the shrinked viewport)
238 bool found = m_associatedWebView->page()->findText(_lastStringSearched, options);
239 notifyMatch(found);
241 // scroll and reset the page viewport if necessary
242 if (useResizeTrick) {
243 Q_ASSERT(container->verticalScrollBar());
244 // the page has now a usable scroll position ...
245 int scrollPosition = m_associatedWebView->page()->currentFrame()->scrollPosition().y();
246 // ... which needs to be extended by the pages offset (usually the header widget)
247 // NOTICE: QWidget::mapTo() fails as the viewport child position can be negative, so we run ourself
248 QWidget *runner = m_associatedWebView;
249 while (runner->parentWidget() != container->viewport()) {
250 scrollPosition += runner->y();
251 runner = runner->parentWidget();
253 // reset viewport to original size ...
254 m_associatedWebView->page()->setViewportSize(oldVpS);
255 // ... let the user see the change ...
256 m_associatedWebView->setUpdatesEnabled(true);
258 // ... and finally scroll to the desired position
259 if (found)
260 container->verticalScrollBar()->setValue(scrollPosition);
263 if (!found) {
264 QPoint previous_position = m_associatedWebView->page()->currentFrame()->scrollPosition();
265 m_associatedWebView->page()->focusNextPrevChild(true);
266 m_associatedWebView->page()->currentFrame()->setScrollPosition(previous_position);
270 void FindBar::findNext()
272 find(FindBar::Forward);
275 void FindBar::findPrevious()
277 find(FindBar::Backward);
281 void FindBar::matchCaseUpdate()
283 Q_ASSERT(m_associatedWebView);
285 m_associatedWebView->page()->findText(_lastStringSearched, QWebPage::FindBackward);
286 findNext();
287 updateHighlight();
291 void FindBar::updateHighlight()
293 Q_ASSERT(m_associatedWebView);
295 QWebPage::FindFlags options = QWebPage::HighlightAllOccurrences;
297 m_associatedWebView->page()->findText(QString(), options); //Clear an existing highlight
299 if (!isHidden() && highlightAllState())
301 if (matchCase())
302 options |= QWebPage::FindCaseSensitively;
303 m_associatedWebView->page()->findText(_lastStringSearched, options);
307 void FindBar::setAssociatedWebView(QWebView *webView)
309 if (m_associatedWebView)
310 disconnect(m_associatedWebView, nullptr, this, nullptr);
312 m_associatedWebView = webView;
314 if (m_associatedWebView) {
315 // highlighting is fancy, but terribly expensive - disable by default for fat messages
316 if (auto emb = qobject_cast<EmbeddedWebView*>(m_associatedWebView)) {
317 m_highlightAll->setChecked(!emb->staticWidth());
319 // Automatically hide this FindBar widget when the underlying webview goes away
320 connect(m_associatedWebView.data(), &QObject::destroyed,
321 this, &FindBar::resetAssociatedWebView);