VirtualBox

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

Last change on this file since 90719 was 90719, checked in by vboxsync, 3 years ago

FE/Qt: bugref:9831. Replacing backward action icon.

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