VirtualBox

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

Last change on this file since 88439 was 88439, checked in by vboxsync, 4 years ago

FE/Qt: bugref:9831. Clearing overlaymode during zooming.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.2 KB
Line 
1/* $Id: UIHelpViewer.cpp 88439 2021-04-09 15:08:16Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIHelpBrowserWidget class implementation.
4 */
5
6/*
7 * Copyright (C) 2010-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Qt includes: */
19#include <QClipboard>
20#include <QtGlobal>
21#ifdef VBOX_WITH_QHELP_VIEWER
22 #include <QtHelp/QHelpEngine>
23 #include <QtHelp/QHelpContentWidget>
24 #include <QtHelp/QHelpIndexWidget>
25 #include <QtHelp/QHelpSearchEngine>
26 #include <QtHelp/QHelpSearchQueryWidget>
27 #include <QtHelp/QHelpSearchResultWidget>
28#endif
29#include <QLabel>
30#include <QMenu>
31#include <QHBoxLayout>
32#include <QGraphicsBlurEffect>
33#include <QLabel>
34#include <QPainter>
35#include <QScrollBar>
36#include <QTextBlock>
37#include <QWidgetAction>
38#ifdef RT_OS_SOLARIS
39# include <QFontDatabase>
40#endif
41
42/* GUI includes: */
43#include "QIToolButton.h"
44#include "UIHelpViewer.h"
45#include "UIHelpBrowserWidget.h"
46#include "UIIconPool.h"
47#include "UISearchLineEdit.h"
48
49/* COM includes: */
50#include "COMEnums.h"
51#include "CSystemProperties.h"
52
53#ifdef VBOX_WITH_QHELP_VIEWER
54
55static int iZoomPercentageStep = 20;
56const QPair<int, int> UIHelpViewer::zoomPercentageMinMax = QPair<int, int>(20, 300);
57
58
59/*********************************************************************************************************************************
60* UIContextMenuNavigationAction definition. *
61*********************************************************************************************************************************/
62class UIContextMenuNavigationAction : public QWidgetAction
63{
64
65 Q_OBJECT;
66
67signals:
68
69 void sigGoBackward();
70 void sigGoForward();
71 void sigGoHome();
72 void sigAddBookmark();
73
74public:
75
76 UIContextMenuNavigationAction(QObject *pParent = 0);
77 void setBackwardAvailable(bool fAvailable);
78 void setForwardAvailable(bool fAvailable);
79
80private:
81
82 void prepare();
83 QIToolButton *m_pBackwardButton;
84 QIToolButton *m_pForwardButton;
85 QIToolButton *m_pHomeButton;
86 QIToolButton *m_pAddBookmarkButton;
87};
88
89/*********************************************************************************************************************************
90* UIFindInPageWidget definition. *
91*********************************************************************************************************************************/
92class UIFindInPageWidget : public QIWithRetranslateUI<QWidget>
93{
94
95 Q_OBJECT;
96
97signals:
98
99 void sigDragging(const QPoint &delta);
100 void sigSearchTextChanged(const QString &strSearchText);
101 void sigSelectNextMatch();
102 void sigSelectPreviousMatch();
103 void sigClose();
104
105public:
106
107 UIFindInPageWidget(QWidget *pParent = 0);
108 void setMatchCountAndCurrentIndex(int iTotalMatchCount, int iCurrentlyScrolledIndex);
109 void clearSearchField();
110
111protected:
112
113 virtual bool eventFilter(QObject *pObject, QEvent *pEvent) /* override */;
114 virtual void keyPressEvent(QKeyEvent *pEvent) /* override */;
115
116private:
117
118 void prepare();
119 void retranslateUi();
120 UISearchLineEdit *m_pSearchLineEdit;
121 QIToolButton *m_pNextButton;
122 QIToolButton *m_pPreviousButton;
123 QIToolButton *m_pCloseButton;
124 QLabel *m_pDragMoveLabel;
125 QPoint m_previousMousePosition;
126};
127
128
129/*********************************************************************************************************************************
130* UIContextMenuNavigationAction implementation. *
131*********************************************************************************************************************************/
132UIContextMenuNavigationAction::UIContextMenuNavigationAction(QObject *pParent /* = 0 */)
133 :QWidgetAction(pParent)
134 , m_pBackwardButton(0)
135 , m_pForwardButton(0)
136 , m_pHomeButton(0)
137 , m_pAddBookmarkButton(0)
138{
139 prepare();
140}
141
142void UIContextMenuNavigationAction::setBackwardAvailable(bool fAvailable)
143{
144 if (m_pBackwardButton)
145 m_pBackwardButton->setEnabled(fAvailable);
146}
147
148void UIContextMenuNavigationAction::setForwardAvailable(bool fAvailable)
149{
150 if (m_pForwardButton)
151 m_pForwardButton->setEnabled(fAvailable);
152}
153
154void UIContextMenuNavigationAction::prepare()
155{
156 QWidget *pWidget = new QWidget;
157 setDefaultWidget(pWidget);
158 QHBoxLayout *pMainLayout = new QHBoxLayout(pWidget);
159 AssertReturnVoid(pMainLayout);
160
161 m_pBackwardButton = new QIToolButton;
162 m_pForwardButton = new QIToolButton;
163 m_pHomeButton = new QIToolButton;
164 m_pAddBookmarkButton = new QIToolButton;
165
166 AssertReturnVoid(m_pBackwardButton &&
167 m_pForwardButton &&
168 m_pHomeButton);
169 m_pForwardButton->setEnabled(false);
170 m_pBackwardButton->setEnabled(false);
171 m_pHomeButton->setIcon(UIIconPool::iconSet(":/help_browser_home_32px.png"));
172 m_pForwardButton->setIcon(UIIconPool::iconSet(":/help_browser_forward_32px.png", ":/help_browser_forward_disabled_32px.png"));
173 m_pBackwardButton->setIcon(UIIconPool::iconSet(":/help_browser_backward_32px.png", ":/help_browser_backward_disabled_32px.png"));
174 m_pAddBookmarkButton->setIcon(UIIconPool::iconSet(":/help_browser_add_bookmark.png"));
175
176 pMainLayout->addWidget(m_pBackwardButton);
177 pMainLayout->addWidget(m_pForwardButton);
178 pMainLayout->addWidget(m_pHomeButton);
179 pMainLayout->addWidget(m_pAddBookmarkButton);
180 pMainLayout->setContentsMargins(0, 0, 0, 0);
181
182 connect(m_pBackwardButton, &QIToolButton::pressed,
183 this, &UIContextMenuNavigationAction::sigGoBackward);
184 connect(m_pForwardButton, &QIToolButton::pressed,
185 this, &UIContextMenuNavigationAction::sigGoForward);
186 connect(m_pHomeButton, &QIToolButton::pressed,
187 this, &UIContextMenuNavigationAction::sigGoHome);
188 connect(m_pAddBookmarkButton, &QIToolButton::pressed,
189 this, &UIContextMenuNavigationAction::sigAddBookmark);
190}
191
192
193/*********************************************************************************************************************************
194* UIFindInPageWidget implementation. *
195*********************************************************************************************************************************/
196UIFindInPageWidget::UIFindInPageWidget(QWidget *pParent /* = 0 */)
197 : QIWithRetranslateUI<QWidget>(pParent)
198 , m_pSearchLineEdit(0)
199 , m_pNextButton(0)
200 , m_pPreviousButton(0)
201 , m_pCloseButton(0)
202 , m_previousMousePosition(-1, -1)
203{
204 prepare();
205}
206
207void UIFindInPageWidget::setMatchCountAndCurrentIndex(int iTotalMatchCount, int iCurrentlyScrolledIndex)
208{
209 if (!m_pSearchLineEdit)
210 return;
211 m_pSearchLineEdit->setMatchCount(iTotalMatchCount);
212 m_pSearchLineEdit->setScrollToIndex(iCurrentlyScrolledIndex);
213}
214
215void UIFindInPageWidget::clearSearchField()
216{
217 if (!m_pSearchLineEdit)
218 return;
219 m_pSearchLineEdit->blockSignals(true);
220 m_pSearchLineEdit->reset();
221 m_pSearchLineEdit->blockSignals(false);
222}
223
224bool UIFindInPageWidget::eventFilter(QObject *pObject, QEvent *pEvent)
225{
226 if (pObject == m_pDragMoveLabel)
227 {
228 if (pEvent->type() == QEvent::Enter)
229 m_pDragMoveLabel->setCursor(Qt::CrossCursor);
230 else if (pEvent->type() == QEvent::Leave)
231 {
232 if (parentWidget())
233 m_pDragMoveLabel->setCursor(parentWidget()->cursor());
234 }
235 else if (pEvent->type() == QEvent::MouseMove)
236 {
237 QMouseEvent *pMouseEvent = static_cast<QMouseEvent*>(pEvent);
238 if (pMouseEvent->buttons() == Qt::LeftButton)
239 {
240 if (m_previousMousePosition != QPoint(-1, -1))
241 emit sigDragging(pMouseEvent->globalPos() - m_previousMousePosition);
242 m_previousMousePosition = pMouseEvent->globalPos();
243 m_pDragMoveLabel->setCursor(Qt::ClosedHandCursor);
244 }
245 }
246 else if (pEvent->type() == QEvent::MouseButtonRelease)
247 {
248 m_previousMousePosition = QPoint(-1, -1);
249 m_pDragMoveLabel->setCursor(Qt::CrossCursor);
250 }
251 }
252 return QIWithRetranslateUI<QWidget>::eventFilter(pObject, pEvent);
253}
254
255void UIFindInPageWidget::keyPressEvent(QKeyEvent *pEvent)
256{
257 switch (pEvent->key())
258 {
259 case Qt::Key_Escape:
260 emit sigClose();
261 return;
262 break;
263 case Qt::Key_Down:
264 emit sigSelectNextMatch();
265 return;
266 break;
267 case Qt::Key_Up:
268 emit sigSelectPreviousMatch();
269 return;
270 break;
271 default:
272 QIWithRetranslateUI<QWidget>::keyPressEvent(pEvent);
273 break;
274 }
275}
276
277void UIFindInPageWidget::prepare()
278{
279 setAutoFillBackground(true);
280 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
281
282 QHBoxLayout *pLayout = new QHBoxLayout(this);
283 m_pSearchLineEdit = new UISearchLineEdit;
284 AssertReturnVoid(pLayout && m_pSearchLineEdit);
285 setFocusProxy(m_pSearchLineEdit);
286 QFontMetrics fontMetric(m_pSearchLineEdit->font());
287 setMinimumSize(40 * fontMetric.width("x"),
288 fontMetric.height() +
289 qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin) +
290 qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin));
291
292 connect(m_pSearchLineEdit, &UISearchLineEdit::textChanged,
293 this, &UIFindInPageWidget::sigSearchTextChanged);
294
295 m_pDragMoveLabel = new QLabel;
296 AssertReturnVoid(m_pDragMoveLabel);
297 m_pDragMoveLabel->installEventFilter(this);
298 m_pDragMoveLabel->setPixmap(QPixmap(":/drag_move_16px.png"));
299 pLayout->addWidget(m_pDragMoveLabel);
300
301
302 pLayout->setSpacing(0);
303 pLayout->addWidget(m_pSearchLineEdit);
304
305 m_pPreviousButton = new QIToolButton;
306 m_pNextButton = new QIToolButton;
307 m_pCloseButton = new QIToolButton;
308
309 pLayout->addWidget(m_pPreviousButton);
310 pLayout->addWidget(m_pNextButton);
311 pLayout->addWidget(m_pCloseButton);
312
313 m_pPreviousButton->setIcon(UIIconPool::iconSet(":/arrow_up_10px.png"));
314 m_pNextButton->setIcon(UIIconPool::iconSet(":/arrow_down_10px.png"));
315 m_pCloseButton->setIcon(UIIconPool::iconSet(":/close_16px.png"));
316
317 connect(m_pPreviousButton, &QIToolButton::pressed, this, &UIFindInPageWidget::sigSelectPreviousMatch);
318 connect(m_pNextButton, &QIToolButton::pressed, this, &UIFindInPageWidget::sigSelectNextMatch);
319 connect(m_pCloseButton, &QIToolButton::pressed, this, &UIFindInPageWidget::sigClose);
320}
321
322void UIFindInPageWidget::retranslateUi()
323{
324}
325
326
327/*********************************************************************************************************************************
328* UIHelpViewer implementation. *
329*********************************************************************************************************************************/
330
331UIHelpViewer::UIHelpViewer(const QHelpEngine *pHelpEngine, QWidget *pParent /* = 0 */)
332 :QIWithRetranslateUI<QTextBrowser>(pParent)
333 , m_pHelpEngine(pHelpEngine)
334 , m_pFindInPageWidget(new UIFindInPageWidget(this))
335 , m_fFindWidgetDragged(false)
336 , m_iMarginForFindWidget(qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin))
337 , m_iSelectedMatchIndex(0)
338 , m_iSearchTermLength(0)
339 , m_iZoomPercentage(100)
340 , m_fOverlayMode(false)
341 , m_fCursorChanged(false)
342 , m_pOverlayLabel(0)
343{
344 m_iInitialFontPointSize = font().pointSize();
345 setUndoRedoEnabled(true);
346 connect(m_pFindInPageWidget, &UIFindInPageWidget::sigDragging,
347 this, &UIHelpViewer::sltHandleFindWidgetDrag);
348 connect(m_pFindInPageWidget, &UIFindInPageWidget::sigSearchTextChanged,
349 this, &UIHelpViewer::sltHandleFindInPageSearchTextChange);
350
351 connect(m_pFindInPageWidget, &UIFindInPageWidget::sigSelectPreviousMatch,
352 this, &UIHelpViewer::sltSelectPreviousMatch);
353 connect(m_pFindInPageWidget, &UIFindInPageWidget::sigSelectNextMatch,
354 this, &UIHelpViewer::sltSelectNextMatch);
355 connect(m_pFindInPageWidget, &UIFindInPageWidget::sigClose,
356 this, &UIHelpViewer::sigCloseFindInPageWidget);
357
358 m_defaultCursor = cursor();
359 m_handCursor = QCursor(Qt::PointingHandCursor);
360
361 m_pFindInPageWidget->setVisible(false);
362
363 m_pOverlayLabel = new QLabel(this);
364 if (m_pOverlayLabel)
365 {
366 m_pOverlayLabel->hide();
367 m_pOverlayLabel->installEventFilter(this);
368 }
369
370 m_pOverlayBlurEffect = new QGraphicsBlurEffect(this);
371 if (m_pOverlayBlurEffect)
372 {
373 viewport()->setGraphicsEffect(m_pOverlayBlurEffect);
374 m_pOverlayBlurEffect->setEnabled(false);
375 m_pOverlayBlurEffect->setBlurRadius(8);
376 }
377 retranslateUi();
378}
379
380QVariant UIHelpViewer::loadResource(int type, const QUrl &name)
381{
382 if (name.scheme() == "qthelp" && m_pHelpEngine)
383 return QVariant(m_pHelpEngine->fileData(name));
384 else
385 return QTextBrowser::loadResource(type, name);
386}
387
388void UIHelpViewer::emitHistoryChangedSignal()
389{
390 emit historyChanged();
391 emit backwardAvailable(true);
392}
393
394void UIHelpViewer::setSource(const QUrl &url)
395{
396 clearOverlay();
397 QTextBrowser::setSource(url);
398 QTextDocument *pDocument = document();
399 iterateDocumentImages();
400 if (!pDocument || pDocument->isEmpty())
401 setText(tr("<div><p><h3>404. Not found.</h3>The page <b>%1</b> could not be found.</p></div>").arg(url.toString()));
402 if (m_pFindInPageWidget && m_pFindInPageWidget->isVisible())
403 {
404 document()->undo();
405 m_pFindInPageWidget->clearSearchField();
406 }
407 scaleImages();
408}
409
410void UIHelpViewer::sltToggleFindInPageWidget(bool fVisible)
411{
412 if (!m_pFindInPageWidget)
413 return;
414 /* Closing the find in page widget causes QTextBrowser to jump to the top of the document. This hack puts it back into position: */
415 int iPosition = verticalScrollBar()->value();
416 m_iMarginForFindWidget = verticalScrollBar()->width() +
417 qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
418 /* Try to position the widget somewhere meaningful initially: */
419 if (!m_fFindWidgetDragged)
420 m_pFindInPageWidget->move(width() - m_iMarginForFindWidget - m_pFindInPageWidget->width(),
421 m_iMarginForFindWidget);
422
423 m_pFindInPageWidget->setVisible(fVisible);
424
425 if (!fVisible)
426 {
427 document()->undo();
428 m_pFindInPageWidget->clearSearchField();
429 verticalScrollBar()->setValue(iPosition);
430 }
431 else
432 m_pFindInPageWidget->setFocus();
433}
434
435void UIHelpViewer::setFont(const QFont &font)
436{
437 QIWithRetranslateUI<QTextBrowser>::setFont(font);
438 /* Make sure the font size of the find in widget stays constant: */
439 if (m_pFindInPageWidget)
440 {
441 QFont wFont(font);
442 wFont.setPointSize(m_iInitialFontPointSize);
443 m_pFindInPageWidget->setFont(wFont);
444 }
445}
446
447bool UIHelpViewer::isFindInPageWidgetVisible() const
448{
449 if (m_pFindInPageWidget)
450 return m_pFindInPageWidget->isVisible();
451 return false;
452}
453
454void UIHelpViewer::zoom(ZoomOperation enmZoomOperation)
455{
456 clearOverlay();
457 int iPrevZoom = m_iZoomPercentage;
458 switch (enmZoomOperation)
459 {
460 case ZoomOperation_In:
461 iPrevZoom += iZoomPercentageStep;
462 break;
463 case ZoomOperation_Out:
464 iPrevZoom -= iZoomPercentageStep;
465 break;
466 case ZoomOperation_Reset:
467 default:
468 iPrevZoom = 100;
469 break;
470 }
471 setZoomPercentage(iPrevZoom);
472}
473
474void UIHelpViewer::setZoomPercentage(int iZoomPercentage)
475{
476 if (iZoomPercentage > zoomPercentageMinMax.second ||
477 iZoomPercentage < zoomPercentageMinMax.first ||
478 m_iZoomPercentage == iZoomPercentage)
479 return;
480
481 m_iZoomPercentage = iZoomPercentage;
482 scaleFont();
483 scaleImages();
484 emit sigZoomPercentageChanged(m_iZoomPercentage);
485}
486
487void UIHelpViewer::setHelpFileList(const QList<QUrl> &helpFileList)
488{
489 m_helpFileList = helpFileList;
490}
491
492bool UIHelpViewer::isInOverlayMode() const
493{
494 return m_fOverlayMode;
495}
496
497int UIHelpViewer::zoomPercentage() const
498{
499 return m_iZoomPercentage;
500}
501
502void UIHelpViewer::contextMenuEvent(QContextMenuEvent *event)
503{
504 QMenu pMenu;
505
506 UIContextMenuNavigationAction *pNavigationActions = new UIContextMenuNavigationAction;
507 pNavigationActions->setBackwardAvailable(isBackwardAvailable());
508 pNavigationActions->setForwardAvailable(isForwardAvailable());
509
510 connect(pNavigationActions, &UIContextMenuNavigationAction::sigGoBackward,
511 this, &UIHelpViewer::sigGoBackward);
512 connect(pNavigationActions, &UIContextMenuNavigationAction::sigGoForward,
513 this, &UIHelpViewer::sigGoForward);
514 connect(pNavigationActions, &UIContextMenuNavigationAction::sigGoHome,
515 this, &UIHelpViewer::sigGoHome);
516 connect(pNavigationActions, &UIContextMenuNavigationAction::sigAddBookmark,
517 this, &UIHelpViewer::sigAddBookmark);
518
519 QAction *pOpenLinkAction = new QAction(UIHelpBrowserWidget::tr("Open Link"));
520 connect(pOpenLinkAction, &QAction::triggered,
521 this, &UIHelpViewer::sltHandleOpenLink);
522
523 QAction *pOpenInNewTabAction = new QAction(UIHelpBrowserWidget::tr("Open Link in New Tab"));
524 connect(pOpenInNewTabAction, &QAction::triggered,
525 this, &UIHelpViewer::sltHandleOpenLinkInNewTab);
526
527 QAction *pCopyLink = new QAction(UIHelpBrowserWidget::tr("Copy Link"));
528 connect(pCopyLink, &QAction::triggered,
529 this, &UIHelpViewer::sltHandleCopyLink);
530
531
532 QAction *pFindInPage = new QAction(UIHelpBrowserWidget::tr("Find in Page"));
533 pFindInPage->setCheckable(true);
534 if (m_pFindInPageWidget)
535 pFindInPage->setChecked(m_pFindInPageWidget->isVisible());
536 connect(pFindInPage, &QAction::toggled, this, &UIHelpViewer::sltToggleFindInPageWidget);
537
538 pMenu.addAction(pNavigationActions);
539 pMenu.addAction(pOpenLinkAction);
540 pMenu.addAction(pOpenInNewTabAction);
541 pMenu.addAction(pCopyLink);
542 pMenu.addAction(pFindInPage);
543
544 QString strAnchor = anchorAt(event->pos());
545 if (!strAnchor.isEmpty())
546 {
547 QString strLink = source().resolved(anchorAt(event->pos())).toString();
548 pOpenLinkAction->setData(strLink);
549 pOpenInNewTabAction->setData(strLink);
550 pCopyLink->setData(strLink);
551 }
552 else
553 {
554 pOpenLinkAction->setEnabled(false);
555 pOpenInNewTabAction->setEnabled(false);
556 pCopyLink->setEnabled(false);
557 }
558 pMenu.exec(event->globalPos());
559}
560
561void UIHelpViewer::resizeEvent(QResizeEvent *pEvent)
562{
563 clearOverlay();
564 /* Make sure the widget stays inside the parent during parent resize: */
565 if (m_pFindInPageWidget)
566 {
567 if (!isRectInside(m_pFindInPageWidget->geometry(), m_iMarginForFindWidget))
568 moveFindWidgetIn(m_iMarginForFindWidget);
569 }
570 QIWithRetranslateUI<QTextBrowser>::resizeEvent(pEvent);
571}
572
573void UIHelpViewer::wheelEvent(QWheelEvent *pEvent)
574{
575 if (m_fOverlayMode)
576 return;
577 /* QTextBrowser::wheelEvent scales font when some modifiers are pressed. We dont want: */
578 if (pEvent && pEvent->modifiers() == Qt::NoModifier)
579 QTextBrowser::wheelEvent(pEvent);
580}
581
582void UIHelpViewer::mouseReleaseEvent(QMouseEvent *pEvent)
583{
584 clearOverlay();
585
586 QString strAnchor = anchorAt(pEvent->pos());
587 if (!strAnchor.isEmpty())
588 {
589 if ((pEvent->modifiers() & Qt::ControlModifier) ||
590 pEvent->button() == Qt::MidButton)
591 {
592 QString strLink = source().resolved(strAnchor).toString();
593 emit sigOpenLinkInNewTab(strLink, true);
594 return;
595 }
596 }
597 QIWithRetranslateUI<QTextBrowser>::mouseReleaseEvent(pEvent);
598
599 loadImageAtPosition(pEvent->globalPos());
600}
601
602void UIHelpViewer::mousePressEvent(QMouseEvent *pEvent)
603{
604 clearOverlay();
605 QIWithRetranslateUI<QTextBrowser>::mousePressEvent(pEvent);
606 loadImageAtPosition(pEvent->globalPos());
607}
608
609
610void UIHelpViewer::mouseMoveEvent(QMouseEvent *pEvent)
611{
612 if (m_fOverlayMode)
613 return;
614
615 QPoint viewportCoordinates = viewport()->mapFromGlobal(pEvent->globalPos());
616 QTextCursor cursor = cursorForPosition(viewportCoordinates);
617 if (!m_fCursorChanged && cursor.charFormat().isImageFormat())
618 {
619 m_fCursorChanged = true;
620 viewport()->setCursor(m_handCursor);
621 }
622 if (m_fCursorChanged && !cursor.charFormat().isImageFormat())
623 {
624 viewport()->setCursor(m_defaultCursor);
625 m_fCursorChanged = false;
626 }
627 QIWithRetranslateUI<QTextBrowser>::mouseMoveEvent(pEvent);
628}
629
630void UIHelpViewer::mouseDoubleClickEvent(QMouseEvent *pEvent)
631{
632 clearOverlay();
633 QIWithRetranslateUI<QTextBrowser>::mouseDoubleClickEvent(pEvent);
634}
635
636void UIHelpViewer::paintEvent(QPaintEvent *pEvent)
637{
638 QIWithRetranslateUI<QTextBrowser>::paintEvent(pEvent);
639
640 if (m_pOverlayLabel)
641 {
642 if (m_fOverlayMode)
643 {
644 QSize size(0.8 * width(), 0.8 * height());
645 m_pOverlayLabel->setPixmap(m_overlayPixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation));
646 int x = 0.5 * (width() - m_pOverlayLabel->width());
647 int y = 0.5 * (height() - m_pOverlayLabel->height());
648 m_pOverlayLabel->move(x, y);
649 m_pOverlayLabel->show();
650 }
651 else
652 m_pOverlayLabel->hide();
653 }
654}
655
656bool UIHelpViewer::eventFilter(QObject *pObject, QEvent *pEvent)
657{
658 if (pObject == m_pOverlayLabel)
659 {
660 if (pEvent->type() == QEvent::MouseButtonPress ||
661 pEvent->type() == QEvent::MouseButtonDblClick)
662 clearOverlay();
663 }
664 return QIWithRetranslateUI<QTextBrowser>::eventFilter(pObject, pEvent);
665}
666
667void UIHelpViewer::keyPressEvent(QKeyEvent *pEvent)
668{
669 if (pEvent && pEvent->key() == Qt::Key_Escape)
670 clearOverlay();
671 QIWithRetranslateUI<QTextBrowser>::keyPressEvent(pEvent);
672}
673
674void UIHelpViewer::retranslateUi()
675{
676}
677
678void UIHelpViewer::moveFindWidgetIn(int iMargin)
679{
680 if (!m_pFindInPageWidget)
681 return;
682
683 QRect rect = m_pFindInPageWidget->geometry();
684 if (rect.left() < iMargin)
685 rect.translate(-rect.left() + iMargin, 0);
686 if (rect.right() > width() - iMargin)
687 rect.translate((width() - iMargin - rect.right()), 0);
688 if (rect.top() < iMargin)
689 rect.translate(0, -rect.top() + iMargin);
690
691 if (rect.bottom() > height() - iMargin)
692 rect.translate(0, (height() - iMargin - rect.bottom()));
693 m_pFindInPageWidget->setGeometry(rect);
694 m_pFindInPageWidget->update();
695}
696
697bool UIHelpViewer::isRectInside(const QRect &rect, int iMargin) const
698{
699 if (rect.left() < iMargin || rect.top() < iMargin)
700 return false;
701 if (rect.right() > width() - iMargin || rect.bottom() > height() - iMargin)
702 return false;
703 return true;
704}
705
706void UIHelpViewer::findAllMatches(const QString &searchString)
707{
708 QTextDocument *pDocument = document();
709 AssertReturnVoid(pDocument);
710
711 m_matchedCursorPosition.clear();
712 if (searchString.isEmpty())
713 return;
714 QTextCursor cursor(pDocument);
715 QTextDocument::FindFlags flags;
716 int iMatchCount = 0;
717 while (!cursor.isNull() && !cursor.atEnd())
718 {
719 cursor = pDocument->find(searchString, cursor, flags);
720 if (!cursor.isNull())
721 {
722 m_matchedCursorPosition << cursor.position() - searchString.length();
723 ++iMatchCount;
724 }
725 }
726}
727
728void UIHelpViewer::highlightFinds(int iSearchTermLength)
729{
730 QTextDocument* pDocument = document();
731 AssertReturnVoid(pDocument);
732 /* Clear previous highlight: */
733 pDocument->undo();
734
735 QTextCursor highlightCursor(pDocument);
736 QTextCursor cursor(pDocument);
737 cursor.beginEditBlock();
738 for (int i = 0; i < m_matchedCursorPosition.size(); ++i)
739 {
740 highlightCursor.setPosition(m_matchedCursorPosition[i]);
741
742 QTextCharFormat colorFormat(highlightCursor.charFormat());
743 colorFormat.setBackground(Qt::yellow);
744
745 highlightCursor.setPosition(m_matchedCursorPosition[i] + iSearchTermLength, QTextCursor::KeepAnchor);
746 if (!highlightCursor.isNull())
747 highlightCursor.setCharFormat(colorFormat);
748 }
749 cursor.endEditBlock();
750}
751
752void UIHelpViewer::selectMatch(int iMatchIndex, int iSearchStringLength)
753{
754 QTextCursor cursor = textCursor();
755 /* Move the cursor to the beginning of the matched string: */
756 cursor.setPosition(m_matchedCursorPosition.at(iMatchIndex), QTextCursor::MoveAnchor);
757 /* Move the cursor to the end of the matched string while keeping the anchor at the begining thus selecting the text: */
758 cursor.setPosition(m_matchedCursorPosition.at(iMatchIndex) + iSearchStringLength, QTextCursor::KeepAnchor);
759 ensureCursorVisible();
760 setTextCursor(cursor);
761}
762
763void UIHelpViewer::sltHandleOpenLinkInNewTab()
764{
765 QAction *pSender = qobject_cast<QAction*>(sender());
766 if (!pSender)
767 return;
768 QUrl url = pSender->data().toUrl();
769 if (url.isValid())
770 emit sigOpenLinkInNewTab(url, false);
771}
772
773void UIHelpViewer::sltHandleOpenLink()
774{
775 QAction *pSender = qobject_cast<QAction*>(sender());
776 if (!pSender)
777 return;
778 QUrl url = pSender->data().toUrl();
779 if (url.isValid())
780 QTextBrowser::setSource(url);
781}
782
783void UIHelpViewer::sltHandleCopyLink()
784{
785 QAction *pSender = qobject_cast<QAction*>(sender());
786 if (!pSender)
787 return;
788 QUrl url = pSender->data().toUrl();
789 if (url.isValid())
790 {
791 QClipboard *pClipboard = QApplication::clipboard();
792 if (pClipboard)
793 pClipboard->setText(url.toString());
794 }
795}
796
797void UIHelpViewer::sltHandleFindWidgetDrag(const QPoint &delta)
798{
799 if (!m_pFindInPageWidget)
800 return;
801 QRect geo = m_pFindInPageWidget->geometry();
802 geo.translate(delta);
803
804 /* Allow the move if m_pFindInPageWidget stays inside after the move: */
805 if (isRectInside(geo, m_iMarginForFindWidget))
806 m_pFindInPageWidget->move(m_pFindInPageWidget->pos() + delta);
807 m_fFindWidgetDragged = true;
808 update();
809}
810
811void UIHelpViewer::sltHandleFindInPageSearchTextChange(const QString &strSearchText)
812{
813 m_iSearchTermLength = strSearchText.length();
814 findAllMatches(strSearchText);
815 highlightFinds(m_iSearchTermLength);
816 //scrollToMatch(int iMatchIndex);
817 selectMatch(0, m_iSearchTermLength);
818 if (m_pFindInPageWidget)
819 m_pFindInPageWidget->setMatchCountAndCurrentIndex(m_matchedCursorPosition.size(), 0);
820}
821
822void UIHelpViewer::sltSelectPreviousMatch()
823{
824 m_iSelectedMatchIndex = m_iSelectedMatchIndex <= 0 ? m_matchedCursorPosition.size() - 1 : (m_iSelectedMatchIndex - 1);
825 selectMatch(m_iSelectedMatchIndex, m_iSearchTermLength);
826 if (m_pFindInPageWidget)
827 m_pFindInPageWidget->setMatchCountAndCurrentIndex(m_matchedCursorPosition.size(), m_iSelectedMatchIndex);
828}
829
830void UIHelpViewer::sltSelectNextMatch()
831{
832 m_iSelectedMatchIndex = m_iSelectedMatchIndex >= m_matchedCursorPosition.size() - 1 ? 0 : (m_iSelectedMatchIndex + 1);
833 selectMatch(m_iSelectedMatchIndex, m_iSearchTermLength);
834 if (m_pFindInPageWidget)
835 m_pFindInPageWidget->setMatchCountAndCurrentIndex(m_matchedCursorPosition.size(), m_iSelectedMatchIndex);
836}
837
838void UIHelpViewer::iterateDocumentImages()
839{
840 m_imageMap.clear();
841 QTextCursor cursor = textCursor();
842 cursor.movePosition(QTextCursor::Start);
843 while (!cursor.atEnd())
844 {
845 cursor.movePosition(QTextCursor::NextCharacter);
846 if (cursor.charFormat().isImageFormat())
847 {
848 DocumentImage image;
849 QTextImageFormat imageFormat = cursor.charFormat().toImageFormat();
850 image.m_fInitialWidth = imageFormat.width();
851 image.m_iPosition = cursor.position();
852 m_imageMap[imageFormat.name()] = image;
853 }
854 }
855}
856
857void UIHelpViewer::scaleFont()
858{
859 QFont mFont = font();
860 mFont.setPointSize(m_iInitialFontPointSize * m_iZoomPercentage / 100.);
861 setFont(mFont);
862}
863
864void UIHelpViewer::scaleImages()
865{
866 for (QMap<QString, DocumentImage>::iterator iterator = m_imageMap.begin();
867 iterator != m_imageMap.end(); ++iterator)
868 {
869 QTextCursor cursor = textCursor();
870 cursor.movePosition(QTextCursor::Start);
871 cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, (*iterator).m_iPosition - 1);
872 if (cursor.isNull())
873 continue;
874 QTextCharFormat format = cursor.charFormat();
875 if (!format.isImageFormat())
876 continue;
877 QTextImageFormat imageFormat = format.toImageFormat();
878 imageFormat.setWidth((*iterator).m_fInitialWidth * m_iZoomPercentage / 100.);
879 cursor.deleteChar();
880 cursor.insertImage(imageFormat);
881 }
882}
883
884void UIHelpViewer::clearOverlay()
885{
886 if (!m_fOverlayMode)
887 return;
888 m_overlayPixmap = QPixmap();
889 m_fOverlayMode = false;
890 if (m_pOverlayBlurEffect)
891 m_pOverlayBlurEffect->setEnabled(false);
892 emit sigOverlayModeChanged(false);
893}
894
895void UIHelpViewer::loadImageAtPosition(const QPoint &globalPosition)
896{
897 clearOverlay();
898 QPoint viewportCoordinates = viewport()->mapFromGlobal(globalPosition);
899 QTextCursor cursor = cursorForPosition(viewportCoordinates);
900 if (!cursor.charFormat().isImageFormat())
901 return;
902
903 QTextImageFormat imageFormat = cursor.charFormat().toImageFormat();
904 QUrl imageFileUrl;
905 foreach (const QUrl &fileUrl, m_helpFileList)
906 {
907 if (fileUrl.toString().contains(imageFormat.name(), Qt::CaseInsensitive))
908 {
909 imageFileUrl = fileUrl;
910 break;
911 }
912 }
913
914 if (!imageFileUrl.isValid())
915 return;
916 QByteArray fileData = m_pHelpEngine->fileData(imageFileUrl);
917 if (!fileData.isEmpty())
918 {
919 m_overlayPixmap.loadFromData(fileData,"PNG");
920 if (!m_overlayPixmap.isNull())
921 {
922 m_fOverlayMode = true;
923 if (m_pOverlayBlurEffect)
924 m_pOverlayBlurEffect->setEnabled(true);
925 viewport()->setCursor(m_defaultCursor);
926 emit sigOverlayModeChanged(true);
927 }
928 }
929}
930
931#include "UIHelpViewer.moc"
932
933#endif /* #ifdef VBOX_WITH_QHELP_VIEWER */
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