VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/helpbrowser/UIHelpViewer.cpp@ 104576

Last change on this file since 104576 was 104576, checked in by vboxsync, 7 months ago

FE/Qt. bugref:9831. Some fixes on URL handling.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.7 KB
Line 
1/* $Id: UIHelpViewer.cpp 104576 2024-05-10 13:42:38Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIHelpViewer class implementation.
4 */
5
6/*
7 * Copyright (C) 2010-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/* Qt includes: */
29#include <QApplication>
30#include <QClipboard>
31#include <QtGlobal>
32#include <QtHelp/QHelpEngine>
33#include <QtHelp/QHelpContentWidget>
34#include <QtHelp/QHelpIndexWidget>
35#include <QtHelp/QHelpSearchEngine>
36#include <QtHelp/QHelpSearchQueryWidget>
37#include <QtHelp/QHelpSearchResultWidget>
38#include <QLabel>
39#include <QMenu>
40#include <QHBoxLayout>
41#include <QGraphicsBlurEffect>
42#include <QLabel>
43#include <QMimeDatabase>
44#include <QPainter>
45#include <QScrollBar>
46#include <QTextBlock>
47#include <QWidgetAction>
48#ifdef RT_OS_SOLARIS
49# include <QFontDatabase>
50#endif
51
52/* GUI includes: */
53#include "QIToolButton.h"
54#include "UICommon.h"
55#include "UIHelpViewer.h"
56#include "UIHelpBrowserWidget.h"
57#include "UIIconPool.h"
58#include "UISearchLineEdit.h"
59
60/* COM includes: */
61#include "CSystemProperties.h"
62
63
64/*********************************************************************************************************************************
65* UIContextMenuNavigationAction definition. *
66*********************************************************************************************************************************/
67class UIContextMenuNavigationAction : public QWidgetAction
68{
69
70 Q_OBJECT;
71
72signals:
73
74 void sigGoBackward();
75 void sigGoForward();
76 void sigGoHome();
77 void sigReloadPage();
78 void sigAddBookmark();
79
80public:
81
82 UIContextMenuNavigationAction(QObject *pParent = 0);
83 void setBackwardAvailable(bool fAvailable);
84 void setForwardAvailable(bool fAvailable);
85
86private slots:
87
88 void sltGoBackward();
89 void sltGoForward();
90 void sltGoHome();
91 void sltReloadPage();
92 void sltAddBookmark();
93
94private:
95
96 void prepare();
97 QIToolButton *m_pBackwardButton;
98 QIToolButton *m_pForwardButton;
99 QIToolButton *m_pHomeButton;
100 QIToolButton *m_pReloadPageButton;
101 QIToolButton *m_pAddBookmarkButton;
102};
103
104/*********************************************************************************************************************************
105* UIFindInPageWidget definition. *
106*********************************************************************************************************************************/
107class UIFindInPageWidget : public QWidget
108{
109
110 Q_OBJECT;
111
112signals:
113
114 void sigDragging(const QPoint &delta);
115 void sigSearchTextChanged(const QString &strSearchText);
116 void sigSelectNextMatch();
117 void sigSelectPreviousMatch();
118 void sigClose();
119
120public:
121
122 UIFindInPageWidget(QWidget *pParent = 0);
123 void setMatchCountAndCurrentIndex(int iTotalMatchCount, int iCurrentlyScrolledIndex);
124 void clearSearchField();
125
126protected:
127
128 virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
129 virtual void keyPressEvent(QKeyEvent *pEvent) RT_OVERRIDE;
130
131private:
132
133 void prepare();
134 UISearchLineEdit *m_pSearchLineEdit;
135 QIToolButton *m_pNextButton;
136 QIToolButton *m_pPreviousButton;
137 QIToolButton *m_pCloseButton;
138 QLabel *m_pDragMoveLabel;
139 QPoint m_previousMousePosition;
140};
141
142
143/*********************************************************************************************************************************
144* UIContextMenuNavigationAction implementation. *
145*********************************************************************************************************************************/
146UIContextMenuNavigationAction::UIContextMenuNavigationAction(QObject *pParent /* = 0 */)
147 :QWidgetAction(pParent)
148 , m_pBackwardButton(0)
149 , m_pForwardButton(0)
150 , m_pHomeButton(0)
151 , m_pReloadPageButton(0)
152 , m_pAddBookmarkButton(0)
153{
154 prepare();
155}
156
157void UIContextMenuNavigationAction::setBackwardAvailable(bool fAvailable)
158{
159 if (m_pBackwardButton)
160 m_pBackwardButton->setEnabled(fAvailable);
161}
162
163void UIContextMenuNavigationAction::setForwardAvailable(bool fAvailable)
164{
165 if (m_pForwardButton)
166 m_pForwardButton->setEnabled(fAvailable);
167}
168
169void UIContextMenuNavigationAction::sltGoBackward()
170{
171 emit sigGoBackward();
172 emit triggered();
173}
174
175void UIContextMenuNavigationAction::sltGoForward()
176{
177 emit sigGoForward();
178 emit triggered();
179}
180
181void UIContextMenuNavigationAction::sltGoHome()
182{
183 emit sigGoHome();
184 emit triggered();
185}
186
187void UIContextMenuNavigationAction::sltReloadPage()
188{
189 emit sigReloadPage();
190 emit triggered();
191}
192
193void UIContextMenuNavigationAction::sltAddBookmark()
194{
195 emit sigAddBookmark();
196 emit triggered();
197}
198
199void UIContextMenuNavigationAction::prepare()
200{
201 QWidget *pWidget = new QWidget;
202 setDefaultWidget(pWidget);
203 QHBoxLayout *pMainLayout = new QHBoxLayout(pWidget);
204 AssertReturnVoid(pMainLayout);
205
206 m_pBackwardButton = new QIToolButton;
207 m_pForwardButton = new QIToolButton;
208 m_pHomeButton = new QIToolButton;
209 m_pReloadPageButton = new QIToolButton;
210 m_pAddBookmarkButton = new QIToolButton;
211
212 AssertReturnVoid(m_pBackwardButton &&
213 m_pForwardButton &&
214 m_pHomeButton &&
215 m_pReloadPageButton);
216
217 m_pForwardButton->setEnabled(false);
218 m_pBackwardButton->setEnabled(false);
219 m_pHomeButton->setIcon(UIIconPool::iconSet(":/help_browser_home_16px.png", ":/help_browser_home_disabled_16px.png"));
220 m_pReloadPageButton->setIcon(UIIconPool::iconSet(":/help_browser_reload_16px.png", ":/help_browser_reload_disabled_16px.png"));
221 m_pForwardButton->setIcon(UIIconPool::iconSet(":/help_browser_forward_16px.png", ":/help_browser_forward_disabled_16px.png"));
222 m_pBackwardButton->setIcon(UIIconPool::iconSet(":/help_browser_backward_16px.png", ":/help_browser_backward_disabled_16px.png"));
223 m_pAddBookmarkButton->setIcon(UIIconPool::iconSet(":/help_browser_add_bookmark_16px.png", ":/help_browser_add_bookmark_disabled_16px.png"));
224
225 m_pHomeButton->setToolTip(UIHelpBrowserWidget::tr("Return to Start Page"));
226 m_pReloadPageButton->setToolTip(UIHelpBrowserWidget::tr("Reload the Current Page"));
227 m_pForwardButton->setToolTip(UIHelpBrowserWidget::tr("Go Forward to Next Page"));
228 m_pBackwardButton->setToolTip(UIHelpBrowserWidget::tr("Go Back to Previous Page"));
229 m_pAddBookmarkButton->setToolTip(UIHelpBrowserWidget::tr("Add a New Bookmark"));
230
231 pMainLayout->addWidget(m_pBackwardButton);
232 pMainLayout->addWidget(m_pForwardButton);
233 pMainLayout->addWidget(m_pHomeButton);
234 pMainLayout->addWidget(m_pReloadPageButton);
235 pMainLayout->addWidget(m_pAddBookmarkButton);
236 pMainLayout->setContentsMargins(0, 0, 0, 0);
237
238 connect(m_pBackwardButton, &QIToolButton::pressed,
239 this, &UIContextMenuNavigationAction::sltGoBackward);
240 connect(m_pForwardButton, &QIToolButton::pressed,
241 this, &UIContextMenuNavigationAction::sltGoForward);
242 connect(m_pHomeButton, &QIToolButton::pressed,
243 this, &UIContextMenuNavigationAction::sltGoHome);
244 connect(m_pReloadPageButton, &QIToolButton::pressed,
245 this, &UIContextMenuNavigationAction::sltReloadPage);
246 connect(m_pAddBookmarkButton, &QIToolButton::pressed,
247 this, &UIContextMenuNavigationAction::sltAddBookmark);
248 connect(m_pReloadPageButton, &QIToolButton::pressed,
249 this, &UIContextMenuNavigationAction::sltAddBookmark);
250}
251
252
253/*********************************************************************************************************************************
254* UIFindInPageWidget implementation. *
255*********************************************************************************************************************************/
256UIFindInPageWidget::UIFindInPageWidget(QWidget *pParent /* = 0 */)
257 : QWidget(pParent)
258 , m_pSearchLineEdit(0)
259 , m_pNextButton(0)
260 , m_pPreviousButton(0)
261 , m_pCloseButton(0)
262 , m_previousMousePosition(-1, -1)
263{
264 prepare();
265}
266
267void UIFindInPageWidget::setMatchCountAndCurrentIndex(int iTotalMatchCount, int iCurrentlyScrolledIndex)
268{
269 if (!m_pSearchLineEdit)
270 return;
271 m_pSearchLineEdit->setMatchCount(iTotalMatchCount);
272 m_pSearchLineEdit->setScrollToIndex(iCurrentlyScrolledIndex);
273}
274
275void UIFindInPageWidget::clearSearchField()
276{
277 if (!m_pSearchLineEdit)
278 return;
279 m_pSearchLineEdit->blockSignals(true);
280 m_pSearchLineEdit->reset();
281 m_pSearchLineEdit->blockSignals(false);
282}
283
284bool UIFindInPageWidget::eventFilter(QObject *pObject, QEvent *pEvent)
285{
286 if (pObject == m_pDragMoveLabel)
287 {
288 if (pEvent->type() == QEvent::Enter)
289 m_pDragMoveLabel->setCursor(Qt::CrossCursor);
290 else if (pEvent->type() == QEvent::Leave)
291 {
292 if (parentWidget())
293 m_pDragMoveLabel->setCursor(parentWidget()->cursor());
294 }
295 else if (pEvent->type() == QEvent::MouseMove)
296 {
297 QMouseEvent *pMouseEvent = static_cast<QMouseEvent*>(pEvent);
298 const QPoint gPos = pMouseEvent->globalPosition().toPoint();
299 if (pMouseEvent->buttons() == Qt::LeftButton)
300 {
301 if (m_previousMousePosition != QPoint(-1, -1))
302 emit sigDragging(gPos - m_previousMousePosition);
303 m_previousMousePosition = gPos;
304 m_pDragMoveLabel->setCursor(Qt::ClosedHandCursor);
305 }
306 }
307 else if (pEvent->type() == QEvent::MouseButtonRelease)
308 {
309 m_previousMousePosition = QPoint(-1, -1);
310 m_pDragMoveLabel->setCursor(Qt::CrossCursor);
311 }
312 }
313 return QWidget::eventFilter(pObject, pEvent);
314}
315
316void UIFindInPageWidget::keyPressEvent(QKeyEvent *pEvent)
317{
318 switch (pEvent->key())
319 {
320 case Qt::Key_Escape:
321 emit sigClose();
322 return;
323 break;
324 case Qt::Key_Down:
325 emit sigSelectNextMatch();
326 return;
327 break;
328 case Qt::Key_Up:
329 emit sigSelectPreviousMatch();
330 return;
331 break;
332 default:
333 QWidget::keyPressEvent(pEvent);
334 break;
335 }
336}
337
338void UIFindInPageWidget::prepare()
339{
340 setAutoFillBackground(true);
341 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
342
343 QHBoxLayout *pLayout = new QHBoxLayout(this);
344 m_pSearchLineEdit = new UISearchLineEdit;
345 AssertReturnVoid(pLayout && m_pSearchLineEdit);
346 setFocusProxy(m_pSearchLineEdit);
347 QFontMetrics fontMetric(m_pSearchLineEdit->font());
348#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
349 setMinimumSize(40 * fontMetric.horizontalAdvance("x"),
350 fontMetric.height() +
351 qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin) +
352 qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin));
353
354#else
355 setMinimumSize(40 * fontMetric.width("x"),
356 fontMetric.height() +
357 qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin) +
358 qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin));
359#endif
360 connect(m_pSearchLineEdit, &UISearchLineEdit::textChanged,
361 this, &UIFindInPageWidget::sigSearchTextChanged);
362
363 m_pDragMoveLabel = new QLabel;
364 AssertReturnVoid(m_pDragMoveLabel);
365 m_pDragMoveLabel->installEventFilter(this);
366 m_pDragMoveLabel->setPixmap(QPixmap(":/drag_move_16px.png"));
367 pLayout->addWidget(m_pDragMoveLabel);
368
369
370 pLayout->setSpacing(0);
371 pLayout->addWidget(m_pSearchLineEdit);
372
373 m_pPreviousButton = new QIToolButton;
374 m_pNextButton = new QIToolButton;
375 m_pCloseButton = new QIToolButton;
376
377 pLayout->addWidget(m_pPreviousButton);
378 pLayout->addWidget(m_pNextButton);
379 pLayout->addWidget(m_pCloseButton);
380
381 m_pPreviousButton->setIcon(UIIconPool::iconSet(":/arrow_up_10px.png"));
382 m_pNextButton->setIcon(UIIconPool::iconSet(":/arrow_down_10px.png"));
383 m_pCloseButton->setIcon(UIIconPool::iconSet(":/close_16px.png"));
384
385 connect(m_pPreviousButton, &QIToolButton::pressed, this, &UIFindInPageWidget::sigSelectPreviousMatch);
386 connect(m_pNextButton, &QIToolButton::pressed, this, &UIFindInPageWidget::sigSelectNextMatch);
387 connect(m_pCloseButton, &QIToolButton::pressed, this, &UIFindInPageWidget::sigClose);
388}
389
390/*********************************************************************************************************************************
391* UIHelpViewer implementation. *
392*********************************************************************************************************************************/
393
394UIHelpViewer::UIHelpViewer(const QHelpEngine *pHelpEngine, QWidget *pParent /* = 0 */)
395 : QTextBrowser(pParent)
396 , m_pHelpEngine(pHelpEngine)
397 , m_pFindInPageWidget(new UIFindInPageWidget(this))
398 , m_fFindWidgetDragged(false)
399 , m_iMarginForFindWidget(qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin))
400 , m_iSelectedMatchIndex(0)
401 , m_iSearchTermLength(0)
402 , m_fOverlayMode(false)
403 , m_pOverlayLabel(0)
404 , m_iZoomPercentage(100)
405{
406 m_iInitialFontPointSize = font().pointSize();
407 setUndoRedoEnabled(true);
408 connect(m_pFindInPageWidget, &UIFindInPageWidget::sigDragging,
409 this, &UIHelpViewer::sltFindWidgetDrag);
410 connect(m_pFindInPageWidget, &UIFindInPageWidget::sigSearchTextChanged,
411 this, &UIHelpViewer::sltFindInPageSearchTextChange);
412
413 connect(m_pFindInPageWidget, &UIFindInPageWidget::sigSelectPreviousMatch,
414 this, &UIHelpViewer::sltSelectPreviousMatch);
415 connect(m_pFindInPageWidget, &UIFindInPageWidget::sigSelectNextMatch,
416 this, &UIHelpViewer::sltSelectNextMatch);
417 connect(m_pFindInPageWidget, &UIFindInPageWidget::sigClose,
418 this, &UIHelpViewer::sltCloseFindInPageWidget);
419 connect(this, &UIHelpViewer::highlighted,
420 this, &UIHelpViewer::sltUpdateHighlightedURL);
421
422 m_pFindInPageWidget->setVisible(false);
423
424 m_pOverlayLabel = new QLabel(this);
425 if (m_pOverlayLabel)
426 {
427 m_pOverlayLabel->hide();
428 m_pOverlayLabel->installEventFilter(this);
429 }
430
431 m_pOverlayBlurEffect = new QGraphicsBlurEffect(this);
432 if (m_pOverlayBlurEffect)
433 {
434 viewport()->setGraphicsEffect(m_pOverlayBlurEffect);
435 m_pOverlayBlurEffect->setEnabled(false);
436 m_pOverlayBlurEffect->setBlurRadius(8);
437 }
438}
439
440QVariant UIHelpViewer::loadResource(int type, const QUrl &name)
441{
442 if (name.scheme() == "qthelp" && m_pHelpEngine)
443 return QVariant(m_pHelpEngine->fileData(name));
444 else
445 return QTextBrowser::loadResource(type, name);
446}
447
448void UIHelpViewer::emitHistoryChangedSignal()
449{
450 emit historyChanged();
451 emit backwardAvailable(true);
452}
453
454void UIHelpViewer::doSetSource(const QUrl &url, QTextDocument::ResourceType type)
455{
456 clearOverlay();
457 if (url.scheme() != "qthelp")
458 return;
459 QTextBrowser::doSetSource(url, type);
460 QTextDocument *pDocument = document();
461 if (!pDocument || pDocument->isEmpty())
462 {
463 setText(UIHelpBrowserWidget::tr("<div><p><h3>Not found.</h3>The page <b>%1</b> could not be found.</p></div>").arg(url.toString()));
464 setDocumentTitle(UIHelpBrowserWidget::tr("Not Found"));
465 }
466 if (m_pFindInPageWidget && m_pFindInPageWidget->isVisible())
467 {
468 document()->undo();
469 m_pFindInPageWidget->clearSearchField();
470 }
471 iterateDocumentImages();
472 scaleImages();
473}
474
475void UIHelpViewer::toggleFindInPageWidget(bool fVisible)
476{
477 if (!m_pFindInPageWidget)
478 return;
479
480 /* Closing the find in page widget causes QTextBrowser to jump to the top of the document. This hack puts it back into position: */
481 int iPosition = verticalScrollBar()->value();
482 m_iMarginForFindWidget = verticalScrollBar()->width() +
483 qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
484 /* Try to position the widget somewhere meaningful initially: */
485 if (!m_fFindWidgetDragged)
486 m_pFindInPageWidget->move(width() - m_iMarginForFindWidget - m_pFindInPageWidget->width(),
487 m_iMarginForFindWidget);
488
489 m_pFindInPageWidget->setVisible(fVisible);
490
491 if (!fVisible)
492 {
493 /* Clear highlights: */
494 setExtraSelections(QList<QTextEdit::ExtraSelection>());
495 m_pFindInPageWidget->clearSearchField();
496 verticalScrollBar()->setValue(iPosition);
497 }
498 else
499 m_pFindInPageWidget->setFocus();
500 emit sigFindInPageWidgetToogle(fVisible);
501}
502
503void UIHelpViewer::reload()
504{
505 setSource(source());
506}
507
508void UIHelpViewer::sltToggleFindInPageWidget(bool fVisible)
509{
510 clearOverlay();
511 toggleFindInPageWidget(fVisible);
512}
513
514void UIHelpViewer::sltCloseFindInPageWidget()
515{
516 sltToggleFindInPageWidget(false);
517}
518
519void UIHelpViewer::sltUpdateHighlightedURL(const QUrl &url)
520{
521 m_highlightedUrl = url;
522}
523
524void UIHelpViewer::setFont(const QFont &font)
525{
526 QTextBrowser::setFont(font);
527 /* Make sure the font size of the find in widget stays constant: */
528 if (m_pFindInPageWidget)
529 {
530 QFont wFont(font);
531 wFont.setPointSize(m_iInitialFontPointSize);
532 m_pFindInPageWidget->setFont(wFont);
533 }
534}
535
536bool UIHelpViewer::isFindInPageWidgetVisible() const
537{
538 if (m_pFindInPageWidget)
539 return m_pFindInPageWidget->isVisible();
540 return false;
541}
542
543void UIHelpViewer::setZoomPercentage(int iZoomPercentage)
544{
545 m_iZoomPercentage = iZoomPercentage;
546 clearOverlay();
547 scaleFont();
548 scaleImages();
549}
550
551void UIHelpViewer::setHelpFileList(const QList<QUrl> &helpFileList)
552{
553 m_helpFileList = helpFileList;
554 /* File list necessary to get the image data from the help engine: */
555 iterateDocumentImages();
556 scaleImages();
557}
558
559bool UIHelpViewer::hasSelectedText() const
560{
561 return textCursor().hasSelection();
562}
563
564void UIHelpViewer::contextMenuEvent(QContextMenuEvent *event)
565{
566 QMenu menu;
567
568 if (textCursor().hasSelection())
569 {
570 QAction *pCopySelectedTextAction = new QAction(UIHelpBrowserWidget::tr("Copy Selected Text"));
571 connect(pCopySelectedTextAction, &QAction::triggered,
572 this, &UIHelpViewer::copy);
573 menu.addAction(pCopySelectedTextAction);
574 menu.addSeparator();
575 }
576
577 UIContextMenuNavigationAction *pNavigationActions = new UIContextMenuNavigationAction;
578 pNavigationActions->setBackwardAvailable(isBackwardAvailable());
579 pNavigationActions->setForwardAvailable(isForwardAvailable());
580
581 connect(pNavigationActions, &UIContextMenuNavigationAction::sigGoBackward,
582 this, &UIHelpViewer::sigGoBackward);
583 connect(pNavigationActions, &UIContextMenuNavigationAction::sigGoForward,
584 this, &UIHelpViewer::sigGoForward);
585 connect(pNavigationActions, &UIContextMenuNavigationAction::sigGoHome,
586 this, &UIHelpViewer::sigGoHome);
587 connect(pNavigationActions, &UIContextMenuNavigationAction::sigReloadPage,
588 this, &UIHelpViewer::reload);
589 connect(pNavigationActions, &UIContextMenuNavigationAction::sigAddBookmark,
590 this, &UIHelpViewer::sigAddBookmark);
591
592 QAction *pOpenLinkAction = new QAction(UIHelpBrowserWidget::tr("Open Link"));
593 connect(pOpenLinkAction, &QAction::triggered,
594 this, &UIHelpViewer::sltOpenLink);
595
596 QAction *pOpenInNewTabAction = new QAction(UIHelpBrowserWidget::tr("Open Link in New Tab"));
597 connect(pOpenInNewTabAction, &QAction::triggered,
598 this, &UIHelpViewer::sltOpenLinkInNewTab);
599
600 QAction *pCopyLink = new QAction(UIHelpBrowserWidget::tr("Copy Link"));
601 connect(pCopyLink, &QAction::triggered,
602 this, &UIHelpViewer::sltCopyLink);
603
604 QAction *pFindInPage = new QAction(UIHelpBrowserWidget::tr("Find in Page"));
605 pFindInPage->setCheckable(true);
606 if (m_pFindInPageWidget)
607 pFindInPage->setChecked(m_pFindInPageWidget->isVisible());
608 connect(pFindInPage, &QAction::toggled, this, &UIHelpViewer::sltToggleFindInPageWidget);
609
610 menu.addAction(pNavigationActions);
611 menu.addAction(pOpenLinkAction);
612 menu.addAction(pOpenInNewTabAction);
613 menu.addAction(pCopyLink);
614 menu.addAction(pFindInPage);
615
616 if (!m_highlightedUrl.isEmpty())
617 {
618 QString strLink = m_highlightedUrl.toString();
619 pOpenLinkAction->setData(strLink);
620 pOpenInNewTabAction->setData(strLink);
621 pCopyLink->setData(strLink);
622 if (m_highlightedUrl.scheme() == "https" ||
623 m_highlightedUrl.scheme() == "http")
624 pOpenInNewTabAction->setEnabled(false);
625 }
626 else
627 {
628 pOpenLinkAction->setEnabled(false);
629 pOpenInNewTabAction->setEnabled(false);
630 pCopyLink->setEnabled(false);
631 }
632
633 menu.exec(event->globalPos());
634}
635
636void UIHelpViewer::resizeEvent(QResizeEvent *pEvent)
637{
638 if (m_fOverlayMode)
639 clearOverlay();
640 /* Make sure the widget stays inside the parent during parent resize: */
641 if (m_pFindInPageWidget)
642 {
643 if (!isRectInside(m_pFindInPageWidget->geometry(), m_iMarginForFindWidget))
644 moveFindWidgetIn(m_iMarginForFindWidget);
645 }
646 QTextBrowser::resizeEvent(pEvent);
647}
648
649void UIHelpViewer::wheelEvent(QWheelEvent *pEvent)
650{
651 if (m_fOverlayMode && !pEvent)
652 return;
653 /* QTextBrowser::wheelEvent scales font when some modifiers are pressed. We dont want that: */
654 if (pEvent->modifiers() == Qt::NoModifier)
655 QTextBrowser::wheelEvent(pEvent);
656 else if (pEvent->modifiers() & Qt::ControlModifier)
657 {
658 if (pEvent->angleDelta().y() > 0)
659 emit sigZoomRequest(ZoomOperation_In);
660 else if (pEvent->angleDelta().y() < 0)
661 emit sigZoomRequest(ZoomOperation_Out);
662 }
663}
664
665void UIHelpViewer::mouseReleaseEvent(QMouseEvent *pEvent)
666{
667 /* If overlay mode is active just clear it and return: */
668 bool fOverlayMode = m_fOverlayMode;
669 clearOverlay();
670 if (fOverlayMode)
671 return;
672 QString strAnchor = anchorAt(pEvent->position().toPoint());
673
674 if (!strAnchor.isEmpty())
675 {
676 QString strLink = source().resolved(strAnchor).toString();
677 QFileInfo fInfo(strLink);
678 QMimeDatabase base;
679 QMimeType type = base.mimeTypeForFile(fInfo);
680 if (type.isValid() && type.inherits("image/png"))
681 {
682 loadImage(source().resolved(strAnchor));
683 return;
684 }
685 if (source().resolved(strAnchor).scheme() != "qthelp" && pEvent->button() == Qt::LeftButton)
686 {
687 uiCommon().openURL(strLink);
688 return;
689 }
690
691 if ((pEvent->modifiers() & Qt::ControlModifier) ||
692 pEvent->button() == Qt::MiddleButton)
693 {
694
695 emit sigOpenLinkInNewTab(strLink, true);
696 return;
697 }
698 }
699 QTextBrowser::mousePressEvent(pEvent);
700}
701
702void UIHelpViewer::mousePressEvent(QMouseEvent *pEvent)
703{
704 QTextBrowser::mousePressEvent(pEvent);
705}
706
707void UIHelpViewer::mouseMoveEvent(QMouseEvent *pEvent)
708{
709 /*if (m_fOverlayMode)
710 return;*/
711 QTextBrowser::mouseMoveEvent(pEvent);
712}
713
714void UIHelpViewer::mouseDoubleClickEvent(QMouseEvent *pEvent)
715{
716 clearOverlay();
717 QTextBrowser::mouseDoubleClickEvent(pEvent);
718}
719
720void UIHelpViewer::paintEvent(QPaintEvent *pEvent)
721{
722 QTextBrowser::paintEvent(pEvent);
723 QPainter painter(viewport());
724 foreach(const DocumentImage &image, m_imageMap)
725 {
726 QRect rect = cursorRect(image.m_textCursor);
727 QPixmap newPixmap = image.m_pixmap.scaledToWidth(image.m_fScaledWidth, Qt::SmoothTransformation);
728 QRectF imageRect(rect.x() - newPixmap.width(), rect.y(), newPixmap.width(), newPixmap.height());
729
730 int iMargin = 3;
731 QRectF fillRect(imageRect.x() - iMargin, imageRect.y() - iMargin,
732 imageRect.width() + 2 * iMargin, imageRect.height() + 2 * iMargin);
733 /** @todo I need to find the default color somehow and replace hard coded Qt::white. */
734 painter.fillRect(fillRect, Qt::white);
735 painter.drawPixmap(imageRect, newPixmap, newPixmap.rect());
736 }
737}
738
739bool UIHelpViewer::eventFilter(QObject *pObject, QEvent *pEvent)
740{
741 if (pObject == m_pOverlayLabel)
742 {
743 if (pEvent->type() == QEvent::MouseButtonPress ||
744 pEvent->type() == QEvent::MouseButtonDblClick)
745 clearOverlay();
746 }
747 return QTextBrowser::eventFilter(pObject, pEvent);
748}
749
750void UIHelpViewer::keyPressEvent(QKeyEvent *pEvent)
751{
752 if (pEvent && pEvent->key() == Qt::Key_Escape)
753 clearOverlay();
754 if (pEvent && pEvent->modifiers() &Qt::ControlModifier)
755 {
756 switch (pEvent->key())
757 {
758 case Qt::Key_Equal:
759 emit sigZoomRequest(ZoomOperation_In);
760 break;
761 case Qt::Key_Minus:
762 emit sigZoomRequest(ZoomOperation_Out);
763 break;
764 case Qt::Key_0:
765 emit sigZoomRequest(ZoomOperation_Reset);
766 break;
767 default:
768 break;
769 }
770 }
771 QTextBrowser::keyPressEvent(pEvent);
772}
773
774void UIHelpViewer::moveFindWidgetIn(int iMargin)
775{
776 if (!m_pFindInPageWidget)
777 return;
778
779 QRect rect = m_pFindInPageWidget->geometry();
780 if (rect.left() < iMargin)
781 rect.translate(-rect.left() + iMargin, 0);
782 if (rect.right() > width() - iMargin)
783 rect.translate((width() - iMargin - rect.right()), 0);
784 if (rect.top() < iMargin)
785 rect.translate(0, -rect.top() + iMargin);
786
787 if (rect.bottom() > height() - iMargin)
788 rect.translate(0, (height() - iMargin - rect.bottom()));
789 m_pFindInPageWidget->setGeometry(rect);
790 m_pFindInPageWidget->update();
791}
792
793bool UIHelpViewer::isRectInside(const QRect &rect, int iMargin) const
794{
795 if (rect.left() < iMargin || rect.top() < iMargin)
796 return false;
797 if (rect.right() > width() - iMargin || rect.bottom() > height() - iMargin)
798 return false;
799 return true;
800}
801
802void UIHelpViewer::findAllMatches(const QString &searchString)
803{
804 QTextDocument *pDocument = document();
805 AssertReturnVoid(pDocument);
806
807 m_matchedCursorPosition.clear();
808 if (searchString.isEmpty())
809 return;
810 QTextCursor cursor(pDocument);
811 QTextDocument::FindFlags flags;
812 while (!cursor.isNull() && !cursor.atEnd())
813 {
814 cursor = pDocument->find(searchString, cursor, flags);
815 if (!cursor.isNull())
816 m_matchedCursorPosition << cursor.position() - searchString.length();
817 }
818}
819
820void UIHelpViewer::highlightFinds(int iSearchTermLength)
821{
822 QList<QTextEdit::ExtraSelection> extraSelections;
823 for (int i = 0; i < m_matchedCursorPosition.size(); ++i)
824 {
825 QTextEdit::ExtraSelection selection;
826 QTextCursor cursor = textCursor();
827 cursor.setPosition(m_matchedCursorPosition[i]);
828 cursor.setPosition(m_matchedCursorPosition[i] + iSearchTermLength, QTextCursor::KeepAnchor);
829 QTextCharFormat format = cursor.charFormat();
830 format.setBackground(Qt::yellow);
831
832 selection.cursor = cursor;
833 selection.format = format;
834 extraSelections.append(selection);
835 }
836 setExtraSelections(extraSelections);
837}
838
839void UIHelpViewer::selectMatch(int iMatchIndex, int iSearchStringLength)
840{
841 QTextCursor cursor = textCursor();
842 /* Move the cursor to the beginning of the matched string: */
843 cursor.setPosition(m_matchedCursorPosition.at(iMatchIndex), QTextCursor::MoveAnchor);
844 /* Move the cursor to the end of the matched string while keeping the anchor at the begining thus selecting the text: */
845 cursor.setPosition(m_matchedCursorPosition.at(iMatchIndex) + iSearchStringLength, QTextCursor::KeepAnchor);
846 ensureCursorVisible();
847 setTextCursor(cursor);
848}
849
850void UIHelpViewer::sltOpenLinkInNewTab()
851{
852 QAction *pSender = qobject_cast<QAction*>(sender());
853 if (!pSender)
854 return;
855 QUrl url = pSender->data().toUrl();
856 if (url.isValid())
857 emit sigOpenLinkInNewTab(url, false);
858}
859
860void UIHelpViewer::sltOpenLink()
861{
862 QAction *pSender = qobject_cast<QAction*>(sender());
863 if (!pSender)
864 return;
865 QUrl url = pSender->data().toUrl();
866 if (url.isValid())
867 setSource(url);
868}
869
870void UIHelpViewer::sltCopyLink()
871{
872 QAction *pSender = qobject_cast<QAction*>(sender());
873 if (!pSender)
874 return;
875 QUrl url = pSender->data().toUrl();
876 if (url.isValid())
877 {
878 QClipboard *pClipboard = QApplication::clipboard();
879 if (pClipboard)
880 pClipboard->setText(url.toString());
881 }
882}
883
884void UIHelpViewer::sltFindWidgetDrag(const QPoint &delta)
885{
886 if (!m_pFindInPageWidget)
887 return;
888 QRect geo = m_pFindInPageWidget->geometry();
889 geo.translate(delta);
890
891 /* Allow the move if m_pFindInPageWidget stays inside after the move: */
892 if (isRectInside(geo, m_iMarginForFindWidget))
893 m_pFindInPageWidget->move(m_pFindInPageWidget->pos() + delta);
894 m_fFindWidgetDragged = true;
895 update();
896}
897
898void UIHelpViewer::sltFindInPageSearchTextChange(const QString &strSearchText)
899{
900 m_iSearchTermLength = strSearchText.length();
901 findAllMatches(strSearchText);
902 highlightFinds(m_iSearchTermLength);
903 selectMatch(0, m_iSearchTermLength);
904 if (m_pFindInPageWidget)
905 m_pFindInPageWidget->setMatchCountAndCurrentIndex(m_matchedCursorPosition.size(), 0);
906}
907
908void UIHelpViewer::sltSelectPreviousMatch()
909{
910 m_iSelectedMatchIndex = m_iSelectedMatchIndex <= 0 ? m_matchedCursorPosition.size() - 1 : (m_iSelectedMatchIndex - 1);
911 selectMatch(m_iSelectedMatchIndex, m_iSearchTermLength);
912 if (m_pFindInPageWidget)
913 m_pFindInPageWidget->setMatchCountAndCurrentIndex(m_matchedCursorPosition.size(), m_iSelectedMatchIndex);
914}
915
916void UIHelpViewer::sltSelectNextMatch()
917{
918 m_iSelectedMatchIndex = m_iSelectedMatchIndex >= m_matchedCursorPosition.size() - 1 ? 0 : (m_iSelectedMatchIndex + 1);
919 selectMatch(m_iSelectedMatchIndex, m_iSearchTermLength);
920 if (m_pFindInPageWidget)
921 m_pFindInPageWidget->setMatchCountAndCurrentIndex(m_matchedCursorPosition.size(), m_iSelectedMatchIndex);
922}
923
924void UIHelpViewer::iterateDocumentImages()
925{
926 m_imageMap.clear();
927 QTextCursor cursor = textCursor();
928 cursor.movePosition(QTextCursor::Start);
929 while (!cursor.atEnd())
930 {
931 cursor.movePosition(QTextCursor::NextCharacter);
932 if (cursor.charFormat().isImageFormat())
933 {
934 QTextImageFormat imageFormat = cursor.charFormat().toImageFormat();
935 /* There seems to be two cursors per image. Use the first one: */
936 if (m_imageMap.contains(imageFormat.name()))
937 continue;
938 QHash<QString, DocumentImage>::iterator iterator = m_imageMap.insert(imageFormat.name(), DocumentImage());
939 DocumentImage &image = iterator.value();
940 image.m_fInitialWidth = imageFormat.width();
941 image.m_strName = imageFormat.name();
942 image.m_textCursor = cursor;
943 QUrl imageFileUrl;
944 foreach (const QUrl &fileUrl, m_helpFileList)
945 {
946 if (fileUrl.toString().contains(imageFormat.name(), Qt::CaseInsensitive))
947 {
948 imageFileUrl = fileUrl;
949 break;
950 }
951 }
952 if (imageFileUrl.isValid())
953 {
954 QByteArray fileData = m_pHelpEngine->fileData(imageFileUrl);
955 if (!fileData.isEmpty())
956 image.m_pixmap.loadFromData(fileData,"PNG");
957 }
958 }
959 }
960}
961
962void UIHelpViewer::scaleFont()
963{
964 QFont mFont = font();
965 mFont.setPointSize(m_iInitialFontPointSize * m_iZoomPercentage / 100.);
966 setFont(mFont);
967}
968
969void UIHelpViewer::scaleImages()
970{
971 for (QHash<QString, DocumentImage>::iterator iterator = m_imageMap.begin();
972 iterator != m_imageMap.end(); ++iterator)
973 {
974 DocumentImage &image = *iterator;
975 QTextCursor cursor = image.m_textCursor;
976 QTextCharFormat format = cursor.charFormat();
977 if (!format.isImageFormat())
978 continue;
979 QTextImageFormat imageFormat = format.toImageFormat();
980 image.m_fScaledWidth = image.m_fInitialWidth * m_iZoomPercentage / 100.;
981 imageFormat.setWidth(image.m_fScaledWidth);
982 cursor.deletePreviousChar();
983 cursor.deleteChar();
984 cursor.insertImage(imageFormat);
985 }
986}
987
988void UIHelpViewer::clearOverlay()
989{
990 AssertReturnVoid(m_pOverlayLabel);
991
992 if (!m_fOverlayMode)
993 return;
994 m_overlayPixmap = QPixmap();
995 m_fOverlayMode = false;
996 if (m_pOverlayBlurEffect)
997 m_pOverlayBlurEffect->setEnabled(false);
998 m_pOverlayLabel->hide();
999}
1000
1001void UIHelpViewer::enableOverlay()
1002{
1003 AssertReturnVoid(m_pOverlayLabel);
1004 m_fOverlayMode = true;
1005 if (m_pOverlayBlurEffect)
1006 m_pOverlayBlurEffect->setEnabled(true);
1007 toggleFindInPageWidget(false);
1008
1009 /* Scale the image to 1:1 as long as it fits into avaible space (minus some margins and scrollbar sizes): */
1010 int vWidth = 0;
1011 if (verticalScrollBar() && verticalScrollBar()->isVisible())
1012 vWidth = verticalScrollBar()->width();
1013 int hMargin = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) +
1014 qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin) + vWidth;
1015
1016 int hHeight = 0;
1017 if (horizontalScrollBar() && horizontalScrollBar()->isVisible())
1018 hHeight = horizontalScrollBar()->height();
1019 int vMargin = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin) +
1020 qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin) + hHeight;
1021
1022 QSize size(qMin(width() - hMargin, m_overlayPixmap.width()),
1023 qMin(height() - vMargin, m_overlayPixmap.height()));
1024 m_pOverlayLabel->setPixmap(m_overlayPixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation));
1025 m_pOverlayLabel->show();
1026
1027 /* Center the label: */
1028 int x = 0.5 * (width() - vWidth - m_pOverlayLabel->width());
1029 int y = 0.5 * (height() - hHeight - m_pOverlayLabel->height());
1030 m_pOverlayLabel->move(x, y);
1031}
1032
1033void UIHelpViewer::loadImage(const QUrl &imageFileUrl)
1034{
1035 clearOverlay();
1036 /* Dont zoom into image if mouse button released after a mouse drag: */
1037 if (textCursor().hasSelection())
1038 return;
1039 if (!imageFileUrl.isValid())
1040 return;
1041 QByteArray fileData = m_pHelpEngine->fileData(imageFileUrl);
1042 if (!fileData.isEmpty())
1043 {
1044 m_overlayPixmap.loadFromData(fileData,"PNG");
1045 if (!m_overlayPixmap.isNull())
1046 enableOverlay();
1047 }
1048}
1049
1050
1051#include "UIHelpViewer.moc"
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette