VirtualBox

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

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

FE/Qt: bugref:9831. Some improvements on the tab ovelay mode.

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