VirtualBox

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

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

FE/Qt. bugref:9831. Some more fixing related to context menu handling.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.8 KB
Line 
1/* $Id: UIHelpViewer.cpp 104600 2024-05-13 14:26:05Z 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 const QFontMetrics fontMetric(m_pSearchLineEdit->font());
348 setMinimumSize(40 * fontMetric.horizontalAdvance("x"),
349 fontMetric.height() +
350 qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin) +
351 qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin));
352 connect(m_pSearchLineEdit, &UISearchLineEdit::textChanged,
353 this, &UIFindInPageWidget::sigSearchTextChanged);
354
355 m_pDragMoveLabel = new QLabel;
356 AssertReturnVoid(m_pDragMoveLabel);
357 m_pDragMoveLabel->installEventFilter(this);
358 m_pDragMoveLabel->setPixmap(QPixmap(":/drag_move_16px.png"));
359 pLayout->addWidget(m_pDragMoveLabel);
360
361
362 pLayout->setSpacing(0);
363 pLayout->addWidget(m_pSearchLineEdit);
364
365 m_pPreviousButton = new QIToolButton;
366 m_pNextButton = new QIToolButton;
367 m_pCloseButton = new QIToolButton;
368
369 pLayout->addWidget(m_pPreviousButton);
370 pLayout->addWidget(m_pNextButton);
371 pLayout->addWidget(m_pCloseButton);
372
373 m_pPreviousButton->setIcon(UIIconPool::iconSet(":/arrow_up_10px.png"));
374 m_pNextButton->setIcon(UIIconPool::iconSet(":/arrow_down_10px.png"));
375 m_pCloseButton->setIcon(UIIconPool::iconSet(":/close_16px.png"));
376
377 connect(m_pPreviousButton, &QIToolButton::pressed, this, &UIFindInPageWidget::sigSelectPreviousMatch);
378 connect(m_pNextButton, &QIToolButton::pressed, this, &UIFindInPageWidget::sigSelectNextMatch);
379 connect(m_pCloseButton, &QIToolButton::pressed, this, &UIFindInPageWidget::sigClose);
380}
381
382/*********************************************************************************************************************************
383* UIHelpViewer implementation. *
384*********************************************************************************************************************************/
385
386UIHelpViewer::UIHelpViewer(const QHelpEngine *pHelpEngine, QWidget *pParent /* = 0 */)
387 : QTextBrowser(pParent)
388 , m_pHelpEngine(pHelpEngine)
389 , m_pFindInPageWidget(new UIFindInPageWidget(this))
390 , m_fFindWidgetDragged(false)
391 , m_iMarginForFindWidget(qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin))
392 , m_iSelectedMatchIndex(0)
393 , m_iSearchTermLength(0)
394 , m_fOverlayMode(false)
395 , m_pOverlayLabel(0)
396 , m_iZoomPercentage(100)
397{
398 m_iInitialFontPointSize = font().pointSize();
399 setUndoRedoEnabled(true);
400 connect(m_pFindInPageWidget, &UIFindInPageWidget::sigDragging,
401 this, &UIHelpViewer::sltFindWidgetDrag);
402 connect(m_pFindInPageWidget, &UIFindInPageWidget::sigSearchTextChanged,
403 this, &UIHelpViewer::sltFindInPageSearchTextChange);
404
405 connect(m_pFindInPageWidget, &UIFindInPageWidget::sigSelectPreviousMatch,
406 this, &UIHelpViewer::sltSelectPreviousMatch);
407 connect(m_pFindInPageWidget, &UIFindInPageWidget::sigSelectNextMatch,
408 this, &UIHelpViewer::sltSelectNextMatch);
409 connect(m_pFindInPageWidget, &UIFindInPageWidget::sigClose,
410 this, &UIHelpViewer::sltCloseFindInPageWidget);
411 connect(this, &UIHelpViewer::highlighted,
412 this, &UIHelpViewer::sltUpdateHighlightedURL);
413
414 m_pFindInPageWidget->setVisible(false);
415
416 m_pOverlayLabel = new QLabel(this);
417 if (m_pOverlayLabel)
418 {
419 m_pOverlayLabel->hide();
420 m_pOverlayLabel->installEventFilter(this);
421 }
422
423 m_pOverlayBlurEffect = new QGraphicsBlurEffect(this);
424 if (m_pOverlayBlurEffect)
425 {
426 viewport()->setGraphicsEffect(m_pOverlayBlurEffect);
427 m_pOverlayBlurEffect->setEnabled(false);
428 m_pOverlayBlurEffect->setBlurRadius(8);
429 }
430}
431
432QVariant UIHelpViewer::loadResource(int type, const QUrl &name)
433{
434 if (name.scheme() == "qthelp" && m_pHelpEngine)
435 return QVariant(m_pHelpEngine->fileData(name));
436 else
437 return QTextBrowser::loadResource(type, name);
438}
439
440void UIHelpViewer::emitHistoryChangedSignal()
441{
442 emit historyChanged();
443 emit backwardAvailable(true);
444}
445
446void UIHelpViewer::doSetSource(const QUrl &url, QTextDocument::ResourceType type)
447{
448 clearOverlay();
449 if (url.scheme() != "qthelp")
450 return;
451 QTextBrowser::doSetSource(url, type);
452 QTextDocument *pDocument = document();
453 if (!pDocument || pDocument->isEmpty())
454 {
455 setText(UIHelpBrowserWidget::tr("<div><p><h3>Not found.</h3>The page <b>%1</b> could not be found.</p></div>").arg(url.toString()));
456 setDocumentTitle(UIHelpBrowserWidget::tr("Not Found"));
457 }
458 if (m_pFindInPageWidget && m_pFindInPageWidget->isVisible())
459 {
460 document()->undo();
461 m_pFindInPageWidget->clearSearchField();
462 }
463 iterateDocumentImages();
464 scaleImages();
465}
466
467void UIHelpViewer::toggleFindInPageWidget(bool fVisible)
468{
469 if (!m_pFindInPageWidget)
470 return;
471
472 /* Closing the find in page widget causes QTextBrowser to jump to the top of the document. This hack puts it back into position: */
473 int iPosition = verticalScrollBar()->value();
474 m_iMarginForFindWidget = verticalScrollBar()->width() +
475 qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
476 /* Try to position the widget somewhere meaningful initially: */
477 if (!m_fFindWidgetDragged)
478 m_pFindInPageWidget->move(width() - m_iMarginForFindWidget - m_pFindInPageWidget->width(),
479 m_iMarginForFindWidget);
480
481 m_pFindInPageWidget->setVisible(fVisible);
482
483 if (!fVisible)
484 {
485 /* Clear highlights: */
486 setExtraSelections(QList<QTextEdit::ExtraSelection>());
487 m_pFindInPageWidget->clearSearchField();
488 verticalScrollBar()->setValue(iPosition);
489 }
490 else
491 m_pFindInPageWidget->setFocus();
492 emit sigFindInPageWidgetToogle(fVisible);
493}
494
495void UIHelpViewer::reload()
496{
497 setSource(source());
498}
499
500void UIHelpViewer::sltToggleFindInPageWidget(bool fVisible)
501{
502 clearOverlay();
503 toggleFindInPageWidget(fVisible);
504}
505
506void UIHelpViewer::sltCloseFindInPageWidget()
507{
508 sltToggleFindInPageWidget(false);
509}
510
511void UIHelpViewer::sltUpdateHighlightedURL(const QUrl &url)
512{
513 m_highlightedUrl = url;
514}
515
516void UIHelpViewer::setFont(const QFont &font)
517{
518 QTextBrowser::setFont(font);
519 /* Make sure the font size of the find in widget stays constant: */
520 if (m_pFindInPageWidget)
521 {
522 QFont wFont(font);
523 wFont.setPointSize(m_iInitialFontPointSize);
524 m_pFindInPageWidget->setFont(wFont);
525 }
526}
527
528bool UIHelpViewer::isFindInPageWidgetVisible() const
529{
530 if (m_pFindInPageWidget)
531 return m_pFindInPageWidget->isVisible();
532 return false;
533}
534
535void UIHelpViewer::setZoomPercentage(int iZoomPercentage)
536{
537 m_iZoomPercentage = iZoomPercentage;
538 clearOverlay();
539 scaleFont();
540 scaleImages();
541}
542
543void UIHelpViewer::setHelpFileList(const QList<QUrl> &helpFileList)
544{
545 m_helpFileList = helpFileList;
546 /* File list necessary to get the image data from the help engine: */
547 iterateDocumentImages();
548 scaleImages();
549}
550
551bool UIHelpViewer::hasSelectedText() const
552{
553 return textCursor().hasSelection();
554}
555
556void UIHelpViewer::contextMenuEvent(QContextMenuEvent *event)
557{
558 QMenu menu;
559
560 if (textCursor().hasSelection())
561 {
562 QAction *pCopySelectedTextAction = new QAction(UIHelpBrowserWidget::tr("Copy Selected Text"));
563 connect(pCopySelectedTextAction, &QAction::triggered,
564 this, &UIHelpViewer::copy);
565 menu.addAction(pCopySelectedTextAction);
566 menu.addSeparator();
567 }
568
569 UIContextMenuNavigationAction *pNavigationActions = new UIContextMenuNavigationAction;
570 pNavigationActions->setBackwardAvailable(isBackwardAvailable());
571 pNavigationActions->setForwardAvailable(isForwardAvailable());
572
573 connect(pNavigationActions, &UIContextMenuNavigationAction::sigGoBackward,
574 this, &UIHelpViewer::sigGoBackward);
575 connect(pNavigationActions, &UIContextMenuNavigationAction::sigGoForward,
576 this, &UIHelpViewer::sigGoForward);
577 connect(pNavigationActions, &UIContextMenuNavigationAction::sigGoHome,
578 this, &UIHelpViewer::sigGoHome);
579 connect(pNavigationActions, &UIContextMenuNavigationAction::sigReloadPage,
580 this, &UIHelpViewer::reload);
581 connect(pNavigationActions, &UIContextMenuNavigationAction::sigAddBookmark,
582 this, &UIHelpViewer::sigAddBookmark);
583
584 QAction *pOpenLinkAction = new QAction(UIHelpBrowserWidget::tr("Open Link"));
585 connect(pOpenLinkAction, &QAction::triggered,
586 this, &UIHelpViewer::sltOpenLink);
587
588 QAction *pOpenInNewTabAction = new QAction(UIHelpBrowserWidget::tr("Open Link in New Tab"));
589 connect(pOpenInNewTabAction, &QAction::triggered,
590 this, &UIHelpViewer::sltOpenLinkInNewTab);
591
592 QAction *pCopyLink = new QAction(UIHelpBrowserWidget::tr("Copy Link"));
593 connect(pCopyLink, &QAction::triggered,
594 this, &UIHelpViewer::sltCopyLink);
595
596 QAction *pFindInPage = new QAction(UIHelpBrowserWidget::tr("Find in Page"));
597 pFindInPage->setCheckable(true);
598 if (m_pFindInPageWidget)
599 pFindInPage->setChecked(m_pFindInPageWidget->isVisible());
600 connect(pFindInPage, &QAction::toggled, this, &UIHelpViewer::sltToggleFindInPageWidget);
601
602 menu.addAction(pNavigationActions);
603 menu.addAction(pOpenLinkAction);
604 menu.addAction(pOpenInNewTabAction);
605 menu.addAction(pCopyLink);
606 menu.addAction(pFindInPage);
607
608 if (!m_highlightedUrl.isEmpty())
609 {
610 QString strLink = m_highlightedUrl.toString();
611 pOpenLinkAction->setData(strLink);
612 pOpenInNewTabAction->setData(strLink);
613 pCopyLink->setData(strLink);
614 if (m_highlightedUrl.scheme() == "https" ||
615 m_highlightedUrl.scheme() == "http")
616 pOpenInNewTabAction->setEnabled(false);
617 if (isImage(strLink))
618 pOpenInNewTabAction->setEnabled(false);
619 }
620 else
621 {
622 pOpenLinkAction->setEnabled(false);
623 pOpenInNewTabAction->setEnabled(false);
624 pCopyLink->setEnabled(false);
625 }
626
627 menu.exec(event->globalPos());
628}
629
630void UIHelpViewer::resizeEvent(QResizeEvent *pEvent)
631{
632 if (m_fOverlayMode)
633 clearOverlay();
634 /* Make sure the widget stays inside the parent during parent resize: */
635 if (m_pFindInPageWidget)
636 {
637 if (!isRectInside(m_pFindInPageWidget->geometry(), m_iMarginForFindWidget))
638 moveFindWidgetIn(m_iMarginForFindWidget);
639 }
640 QTextBrowser::resizeEvent(pEvent);
641}
642
643void UIHelpViewer::wheelEvent(QWheelEvent *pEvent)
644{
645 if (m_fOverlayMode && !pEvent)
646 return;
647 /* QTextBrowser::wheelEvent scales font when some modifiers are pressed. We dont want that: */
648 if (pEvent->modifiers() == Qt::NoModifier)
649 QTextBrowser::wheelEvent(pEvent);
650 else if (pEvent->modifiers() & Qt::ControlModifier)
651 {
652 if (pEvent->angleDelta().y() > 0)
653 emit sigZoomRequest(ZoomOperation_In);
654 else if (pEvent->angleDelta().y() < 0)
655 emit sigZoomRequest(ZoomOperation_Out);
656 }
657}
658
659void UIHelpViewer::mouseReleaseEvent(QMouseEvent *pEvent)
660{
661 /* If overlay mode is active just clear it and return: */
662 bool fOverlayMode = m_fOverlayMode;
663 clearOverlay();
664 if (fOverlayMode)
665 return;
666 QString strAnchor = anchorAt(pEvent->position().toPoint());
667
668 if (!strAnchor.isEmpty())
669 {
670 QString strLink = source().resolved(strAnchor).toString();
671 if (isImage(strLink))
672 {
673 loadImage(source().resolved(strAnchor));
674 return;
675 }
676 if (source().resolved(strAnchor).scheme() != "qthelp" && pEvent->button() == Qt::LeftButton)
677 {
678 uiCommon().openURL(strLink);
679 return;
680 }
681
682 if ((pEvent->modifiers() & Qt::ControlModifier) ||
683 pEvent->button() == Qt::MiddleButton)
684 {
685
686 emit sigOpenLinkInNewTab(strLink, true);
687 return;
688 }
689 }
690 QTextBrowser::mousePressEvent(pEvent);
691}
692
693void UIHelpViewer::mousePressEvent(QMouseEvent *pEvent)
694{
695 QTextBrowser::mousePressEvent(pEvent);
696}
697
698void UIHelpViewer::mouseMoveEvent(QMouseEvent *pEvent)
699{
700 /*if (m_fOverlayMode)
701 return;*/
702 QTextBrowser::mouseMoveEvent(pEvent);
703}
704
705void UIHelpViewer::mouseDoubleClickEvent(QMouseEvent *pEvent)
706{
707 clearOverlay();
708 QTextBrowser::mouseDoubleClickEvent(pEvent);
709}
710
711void UIHelpViewer::paintEvent(QPaintEvent *pEvent)
712{
713 QTextBrowser::paintEvent(pEvent);
714 QPainter painter(viewport());
715 foreach(const DocumentImage &image, m_imageMap)
716 {
717 QRect rect = cursorRect(image.m_textCursor);
718 QPixmap newPixmap = image.m_pixmap.scaledToWidth(image.m_fScaledWidth, Qt::SmoothTransformation);
719 QRectF imageRect(rect.x() - newPixmap.width(), rect.y(), newPixmap.width(), newPixmap.height());
720
721 int iMargin = 3;
722 QRectF fillRect(imageRect.x() - iMargin, imageRect.y() - iMargin,
723 imageRect.width() + 2 * iMargin, imageRect.height() + 2 * iMargin);
724 /** @todo I need to find the default color somehow and replace hard coded Qt::white. */
725 painter.fillRect(fillRect, Qt::white);
726 painter.drawPixmap(imageRect, newPixmap, newPixmap.rect());
727 }
728}
729
730bool UIHelpViewer::eventFilter(QObject *pObject, QEvent *pEvent)
731{
732 if (pObject == m_pOverlayLabel)
733 {
734 if (pEvent->type() == QEvent::MouseButtonPress ||
735 pEvent->type() == QEvent::MouseButtonDblClick)
736 clearOverlay();
737 }
738 return QTextBrowser::eventFilter(pObject, pEvent);
739}
740
741void UIHelpViewer::keyPressEvent(QKeyEvent *pEvent)
742{
743 if (pEvent && pEvent->key() == Qt::Key_Escape)
744 clearOverlay();
745 if (pEvent && pEvent->modifiers() &Qt::ControlModifier)
746 {
747 switch (pEvent->key())
748 {
749 case Qt::Key_Equal:
750 emit sigZoomRequest(ZoomOperation_In);
751 break;
752 case Qt::Key_Minus:
753 emit sigZoomRequest(ZoomOperation_Out);
754 break;
755 case Qt::Key_0:
756 emit sigZoomRequest(ZoomOperation_Reset);
757 break;
758 default:
759 break;
760 }
761 }
762 QTextBrowser::keyPressEvent(pEvent);
763}
764
765void UIHelpViewer::moveFindWidgetIn(int iMargin)
766{
767 if (!m_pFindInPageWidget)
768 return;
769
770 QRect rect = m_pFindInPageWidget->geometry();
771 if (rect.left() < iMargin)
772 rect.translate(-rect.left() + iMargin, 0);
773 if (rect.right() > width() - iMargin)
774 rect.translate((width() - iMargin - rect.right()), 0);
775 if (rect.top() < iMargin)
776 rect.translate(0, -rect.top() + iMargin);
777
778 if (rect.bottom() > height() - iMargin)
779 rect.translate(0, (height() - iMargin - rect.bottom()));
780 m_pFindInPageWidget->setGeometry(rect);
781 m_pFindInPageWidget->update();
782}
783
784bool UIHelpViewer::isRectInside(const QRect &rect, int iMargin) const
785{
786 if (rect.left() < iMargin || rect.top() < iMargin)
787 return false;
788 if (rect.right() > width() - iMargin || rect.bottom() > height() - iMargin)
789 return false;
790 return true;
791}
792
793void UIHelpViewer::findAllMatches(const QString &searchString)
794{
795 QTextDocument *pDocument = document();
796 AssertReturnVoid(pDocument);
797
798 m_matchedCursorPosition.clear();
799 if (searchString.isEmpty())
800 return;
801 QTextCursor cursor(pDocument);
802 QTextDocument::FindFlags flags;
803 while (!cursor.isNull() && !cursor.atEnd())
804 {
805 cursor = pDocument->find(searchString, cursor, flags);
806 if (!cursor.isNull())
807 m_matchedCursorPosition << cursor.position() - searchString.length();
808 }
809}
810
811void UIHelpViewer::highlightFinds(int iSearchTermLength)
812{
813 QList<QTextEdit::ExtraSelection> extraSelections;
814 for (int i = 0; i < m_matchedCursorPosition.size(); ++i)
815 {
816 QTextEdit::ExtraSelection selection;
817 QTextCursor cursor = textCursor();
818 cursor.setPosition(m_matchedCursorPosition[i]);
819 cursor.setPosition(m_matchedCursorPosition[i] + iSearchTermLength, QTextCursor::KeepAnchor);
820 QTextCharFormat format = cursor.charFormat();
821 format.setBackground(Qt::yellow);
822
823 selection.cursor = cursor;
824 selection.format = format;
825 extraSelections.append(selection);
826 }
827 setExtraSelections(extraSelections);
828}
829
830void UIHelpViewer::selectMatch(int iMatchIndex, int iSearchStringLength)
831{
832 QTextCursor cursor = textCursor();
833 /* Move the cursor to the beginning of the matched string: */
834 cursor.setPosition(m_matchedCursorPosition.at(iMatchIndex), QTextCursor::MoveAnchor);
835 /* Move the cursor to the end of the matched string while keeping the anchor at the begining thus selecting the text: */
836 cursor.setPosition(m_matchedCursorPosition.at(iMatchIndex) + iSearchStringLength, QTextCursor::KeepAnchor);
837 ensureCursorVisible();
838 setTextCursor(cursor);
839}
840
841void UIHelpViewer::sltOpenLinkInNewTab()
842{
843 QAction *pSender = qobject_cast<QAction*>(sender());
844 if (!pSender)
845 return;
846 QUrl url = pSender->data().toUrl();
847 if (url.isValid())
848 emit sigOpenLinkInNewTab(url, false);
849}
850
851void UIHelpViewer::sltOpenLink()
852{
853 QAction *pSender = qobject_cast<QAction*>(sender());
854 if (!pSender)
855 return;
856 QUrl url = pSender->data().toUrl();
857 if (isImage(url.toString()))
858 {
859 loadImage(url);
860 return;
861 }
862 if (url.isValid())
863 setSource(url);
864}
865
866void UIHelpViewer::sltCopyLink()
867{
868 QAction *pSender = qobject_cast<QAction*>(sender());
869 if (!pSender)
870 return;
871 QUrl url = pSender->data().toUrl();
872 if (url.isValid())
873 {
874 QClipboard *pClipboard = QApplication::clipboard();
875 if (pClipboard)
876 pClipboard->setText(url.toString());
877 }
878}
879
880void UIHelpViewer::sltFindWidgetDrag(const QPoint &delta)
881{
882 if (!m_pFindInPageWidget)
883 return;
884 QRect geo = m_pFindInPageWidget->geometry();
885 geo.translate(delta);
886
887 /* Allow the move if m_pFindInPageWidget stays inside after the move: */
888 if (isRectInside(geo, m_iMarginForFindWidget))
889 m_pFindInPageWidget->move(m_pFindInPageWidget->pos() + delta);
890 m_fFindWidgetDragged = true;
891 update();
892}
893
894void UIHelpViewer::sltFindInPageSearchTextChange(const QString &strSearchText)
895{
896 m_iSearchTermLength = strSearchText.length();
897 findAllMatches(strSearchText);
898 highlightFinds(m_iSearchTermLength);
899 selectMatch(0, m_iSearchTermLength);
900 if (m_pFindInPageWidget)
901 m_pFindInPageWidget->setMatchCountAndCurrentIndex(m_matchedCursorPosition.size(), 0);
902}
903
904void UIHelpViewer::sltSelectPreviousMatch()
905{
906 m_iSelectedMatchIndex = m_iSelectedMatchIndex <= 0 ? m_matchedCursorPosition.size() - 1 : (m_iSelectedMatchIndex - 1);
907 selectMatch(m_iSelectedMatchIndex, m_iSearchTermLength);
908 if (m_pFindInPageWidget)
909 m_pFindInPageWidget->setMatchCountAndCurrentIndex(m_matchedCursorPosition.size(), m_iSelectedMatchIndex);
910}
911
912void UIHelpViewer::sltSelectNextMatch()
913{
914 m_iSelectedMatchIndex = m_iSelectedMatchIndex >= m_matchedCursorPosition.size() - 1 ? 0 : (m_iSelectedMatchIndex + 1);
915 selectMatch(m_iSelectedMatchIndex, m_iSearchTermLength);
916 if (m_pFindInPageWidget)
917 m_pFindInPageWidget->setMatchCountAndCurrentIndex(m_matchedCursorPosition.size(), m_iSelectedMatchIndex);
918}
919
920void UIHelpViewer::iterateDocumentImages()
921{
922 m_imageMap.clear();
923 QTextCursor cursor = textCursor();
924 cursor.movePosition(QTextCursor::Start);
925 while (!cursor.atEnd())
926 {
927 cursor.movePosition(QTextCursor::NextCharacter);
928 if (cursor.charFormat().isImageFormat())
929 {
930 QTextImageFormat imageFormat = cursor.charFormat().toImageFormat();
931 /* There seems to be two cursors per image. Use the first one: */
932 if (m_imageMap.contains(imageFormat.name()))
933 continue;
934 QHash<QString, DocumentImage>::iterator iterator = m_imageMap.insert(imageFormat.name(), DocumentImage());
935 DocumentImage &image = iterator.value();
936 image.m_fInitialWidth = imageFormat.width();
937 image.m_strName = imageFormat.name();
938 image.m_textCursor = cursor;
939 QUrl imageFileUrl;
940 foreach (const QUrl &fileUrl, m_helpFileList)
941 {
942 if (fileUrl.toString().contains(imageFormat.name(), Qt::CaseInsensitive))
943 {
944 imageFileUrl = fileUrl;
945 break;
946 }
947 }
948 if (imageFileUrl.isValid())
949 {
950 QByteArray fileData = m_pHelpEngine->fileData(imageFileUrl);
951 if (!fileData.isEmpty())
952 image.m_pixmap.loadFromData(fileData,"PNG");
953 }
954 }
955 }
956}
957
958void UIHelpViewer::scaleFont()
959{
960 QFont mFont = font();
961 mFont.setPointSize(m_iInitialFontPointSize * m_iZoomPercentage / 100.);
962 setFont(mFont);
963}
964
965void UIHelpViewer::scaleImages()
966{
967 for (QHash<QString, DocumentImage>::iterator iterator = m_imageMap.begin();
968 iterator != m_imageMap.end(); ++iterator)
969 {
970 DocumentImage &image = *iterator;
971 QTextCursor cursor = image.m_textCursor;
972 QTextCharFormat format = cursor.charFormat();
973 if (!format.isImageFormat())
974 continue;
975 QTextImageFormat imageFormat = format.toImageFormat();
976 image.m_fScaledWidth = image.m_fInitialWidth * m_iZoomPercentage / 100.;
977 imageFormat.setWidth(image.m_fScaledWidth);
978 cursor.deletePreviousChar();
979 cursor.deleteChar();
980 cursor.insertImage(imageFormat);
981 }
982}
983
984void UIHelpViewer::clearOverlay()
985{
986 AssertReturnVoid(m_pOverlayLabel);
987
988 if (!m_fOverlayMode)
989 return;
990 m_overlayPixmap = QPixmap();
991 m_fOverlayMode = false;
992 if (m_pOverlayBlurEffect)
993 m_pOverlayBlurEffect->setEnabled(false);
994 m_pOverlayLabel->hide();
995}
996
997void UIHelpViewer::enableOverlay()
998{
999 AssertReturnVoid(m_pOverlayLabel);
1000 m_fOverlayMode = true;
1001 if (m_pOverlayBlurEffect)
1002 m_pOverlayBlurEffect->setEnabled(true);
1003 toggleFindInPageWidget(false);
1004
1005 /* Scale the image to 1:1 as long as it fits into avaible space (minus some margins and scrollbar sizes): */
1006 int vWidth = 0;
1007 if (verticalScrollBar() && verticalScrollBar()->isVisible())
1008 vWidth = verticalScrollBar()->width();
1009 int hMargin = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin) +
1010 qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin) + vWidth;
1011
1012 int hHeight = 0;
1013 if (horizontalScrollBar() && horizontalScrollBar()->isVisible())
1014 hHeight = horizontalScrollBar()->height();
1015 int vMargin = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin) +
1016 qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin) + hHeight;
1017
1018 QSize size(qMin(width() - hMargin, m_overlayPixmap.width()),
1019 qMin(height() - vMargin, m_overlayPixmap.height()));
1020 m_pOverlayLabel->setPixmap(m_overlayPixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation));
1021 m_pOverlayLabel->show();
1022
1023 /* Center the label: */
1024 int x = 0.5 * (width() - vWidth - m_pOverlayLabel->width());
1025 int y = 0.5 * (height() - hHeight - m_pOverlayLabel->height());
1026 m_pOverlayLabel->move(x, y);
1027}
1028
1029void UIHelpViewer::loadImage(const QUrl &imageFileUrl)
1030{
1031 clearOverlay();
1032 /* Dont zoom into image if mouse button released after a mouse drag: */
1033 if (textCursor().hasSelection())
1034 return;
1035 if (!imageFileUrl.isValid())
1036 return;
1037 QByteArray fileData = m_pHelpEngine->fileData(imageFileUrl);
1038 if (!fileData.isEmpty())
1039 {
1040 m_overlayPixmap.loadFromData(fileData,"PNG");
1041 if (!m_overlayPixmap.isNull())
1042 enableOverlay();
1043 }
1044}
1045
1046bool UIHelpViewer::isImage(const QString &strLink)
1047{
1048 if (strLink.isEmpty())
1049 return false;
1050 QFileInfo fInfo(strLink);
1051 QMimeDatabase base;
1052 QMimeType type = base.mimeTypeForFile(fInfo);
1053 if (type.isValid() && type.inherits("image/png"))
1054 return true;
1055 return false;
1056}
1057
1058#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