VirtualBox

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

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

FE/Qt: bugref:9831. Implementing click and enlarge thingy

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.6 KB
Line 
1/* $Id: UIHelpViewer.cpp 88423 2021-04-08 18:08:52Z 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_fImageOverlay(false)
348 , m_pOverlayWidget(0)
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
371 m_pOverlayWidget = new QWidget(this);
372 QHBoxLayout *overlayLayout = new QHBoxLayout(m_pOverlayWidget);
373 m_pOverlayLabel = new QLabel;
374 overlayLayout->addWidget(m_pOverlayLabel);
375 m_pOverlayWidget->hide();
376
377 m_pOverlayBlurEffect = new QGraphicsBlurEffect(this);
378 viewport()->setGraphicsEffect(m_pOverlayBlurEffect);
379 m_pOverlayBlurEffect->setEnabled(false);
380 m_pOverlayBlurEffect->setBlurRadius(8);
381 retranslateUi();
382}
383
384QVariant UIHelpViewer::loadResource(int type, const QUrl &name)
385{
386 if (name.scheme() == "qthelp" && m_pHelpEngine)
387 return QVariant(m_pHelpEngine->fileData(name));
388 else
389 return QTextBrowser::loadResource(type, name);
390}
391
392void UIHelpViewer::emitHistoryChangedSignal()
393{
394 emit historyChanged();
395 emit backwardAvailable(true);
396}
397
398void UIHelpViewer::setSource(const QUrl &url)
399{
400 QTextBrowser::setSource(url);
401 QTextDocument *pDocument = document();
402 iterateDocumentImages();
403 if (!pDocument || pDocument->isEmpty())
404 {
405 setText(tr("<div><p><h3>404. Not found.</h3>The page <b>%1</b> could not be found.</p></div>").arg(url.toString()));
406 }
407 if (m_pFindInPageWidget && m_pFindInPageWidget->isVisible())
408 {
409 document()->undo();
410 m_pFindInPageWidget->clearSearchField();
411 }
412 scaleImages();
413}
414
415void UIHelpViewer::sltToggleFindInPageWidget(bool fVisible)
416{
417 if (!m_pFindInPageWidget)
418 return;
419 /* Closing the find in page widget causes QTextBrowser to jump to the top of the document. This hack puts it back into position: */
420 int iPosition = verticalScrollBar()->value();
421 m_iMarginForFindWidget = verticalScrollBar()->width() +
422 qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
423 /* Try to position the widget somewhere meaningful initially: */
424 if (!m_fFindWidgetDragged)
425 m_pFindInPageWidget->move(width() - m_iMarginForFindWidget - m_pFindInPageWidget->width(),
426 m_iMarginForFindWidget);
427
428 m_pFindInPageWidget->setVisible(fVisible);
429
430 if (!fVisible)
431 {
432 document()->undo();
433 m_pFindInPageWidget->clearSearchField();
434 verticalScrollBar()->setValue(iPosition);
435 }
436 else
437 m_pFindInPageWidget->setFocus();
438}
439
440void UIHelpViewer::setFont(const QFont &font)
441{
442 QIWithRetranslateUI<QTextBrowser>::setFont(font);
443 /* Make sure the font size of the find in widget stays constant: */
444 if (m_pFindInPageWidget)
445 {
446 QFont wFont(font);
447 wFont.setPointSize(m_iInitialFontPointSize);
448 m_pFindInPageWidget->setFont(wFont);
449 }
450}
451
452bool UIHelpViewer::isFindInPageWidgetVisible() const
453{
454 if (m_pFindInPageWidget)
455 return m_pFindInPageWidget->isVisible();
456 return false;
457}
458
459void UIHelpViewer::zoom(ZoomOperation enmZoomOperation)
460{
461 int iPrevZoom = m_iZoomPercentage;
462 switch (enmZoomOperation)
463 {
464 case ZoomOperation_In:
465 iPrevZoom += iZoomPercentageStep;
466 break;
467 case ZoomOperation_Out:
468 iPrevZoom -= iZoomPercentageStep;
469 break;
470 case ZoomOperation_Reset:
471 default:
472 iPrevZoom = 100;
473 break;
474 }
475 setZoomPercentage(iPrevZoom);
476}
477
478void UIHelpViewer::setZoomPercentage(int iZoomPercentage)
479{
480 if (iZoomPercentage > zoomPercentageMinMax.second ||
481 iZoomPercentage < zoomPercentageMinMax.first ||
482 m_iZoomPercentage == iZoomPercentage)
483 return;
484
485 m_iZoomPercentage = iZoomPercentage;
486 scaleFont();
487 scaleImages();
488 emit sigZoomPercentageChanged(m_iZoomPercentage);
489}
490
491void UIHelpViewer::setHelpFileList(const QList<QUrl> &helpFileList)
492{
493 m_helpFileList = helpFileList;
494}
495
496int UIHelpViewer::zoomPercentage() const
497{
498 return m_iZoomPercentage;
499}
500
501void UIHelpViewer::contextMenuEvent(QContextMenuEvent *event)
502{
503 QMenu pMenu;
504
505 UIContextMenuNavigationAction *pNavigationActions = new UIContextMenuNavigationAction;
506 pNavigationActions->setBackwardAvailable(isBackwardAvailable());
507 pNavigationActions->setForwardAvailable(isForwardAvailable());
508
509 connect(pNavigationActions, &UIContextMenuNavigationAction::sigGoBackward,
510 this, &UIHelpViewer::sigGoBackward);
511 connect(pNavigationActions, &UIContextMenuNavigationAction::sigGoForward,
512 this, &UIHelpViewer::sigGoForward);
513 connect(pNavigationActions, &UIContextMenuNavigationAction::sigGoHome,
514 this, &UIHelpViewer::sigGoHome);
515 connect(pNavigationActions, &UIContextMenuNavigationAction::sigAddBookmark,
516 this, &UIHelpViewer::sigAddBookmark);
517
518 QAction *pOpenLinkAction = new QAction(UIHelpBrowserWidget::tr("Open Link"));
519 connect(pOpenLinkAction, &QAction::triggered,
520 this, &UIHelpViewer::sltHandleOpenLink);
521
522 QAction *pOpenInNewTabAction = new QAction(UIHelpBrowserWidget::tr("Open Link in New Tab"));
523 connect(pOpenInNewTabAction, &QAction::triggered,
524 this, &UIHelpViewer::sltHandleOpenLinkInNewTab);
525
526 QAction *pCopyLink = new QAction(UIHelpBrowserWidget::tr("Copy Link"));
527 connect(pCopyLink, &QAction::triggered,
528 this, &UIHelpViewer::sltHandleCopyLink);
529
530
531 QAction *pFindInPage = new QAction(UIHelpBrowserWidget::tr("Find in Page"));
532 pFindInPage->setCheckable(true);
533 if (m_pFindInPageWidget)
534 pFindInPage->setChecked(m_pFindInPageWidget->isVisible());
535 connect(pFindInPage, &QAction::toggled, this, &UIHelpViewer::sltToggleFindInPageWidget);
536
537 pMenu.addAction(pNavigationActions);
538 pMenu.addAction(pOpenLinkAction);
539 pMenu.addAction(pOpenInNewTabAction);
540 pMenu.addAction(pCopyLink);
541 pMenu.addAction(pFindInPage);
542
543 QString strAnchor = anchorAt(event->pos());
544 if (!strAnchor.isEmpty())
545 {
546 QString strLink = source().resolved(anchorAt(event->pos())).toString();
547 pOpenLinkAction->setData(strLink);
548 pOpenInNewTabAction->setData(strLink);
549 pCopyLink->setData(strLink);
550 }
551 else
552 {
553 pOpenLinkAction->setEnabled(false);
554 pOpenInNewTabAction->setEnabled(false);
555 pCopyLink->setEnabled(false);
556 }
557 pMenu.exec(event->globalPos());
558}
559
560void UIHelpViewer::resizeEvent(QResizeEvent *pEvent)
561{
562 /* Make sure the widget stays inside the parent during parent resize: */
563 if (m_pFindInPageWidget)
564 {
565 if (!isRectInside(m_pFindInPageWidget->geometry(), m_iMarginForFindWidget))
566 moveFindWidgetIn(m_iMarginForFindWidget);
567 }
568 QIWithRetranslateUI<QTextBrowser>::resizeEvent(pEvent);
569}
570
571void UIHelpViewer::wheelEvent(QWheelEvent *pEvent)
572{
573 if (m_fImageOverlay)
574 return;
575
576 /* QTextBrowser::wheelEvent scales font when some modifiers are pressed. We dont want: */
577 if (pEvent && pEvent->modifiers() == Qt::NoModifier)
578 QTextBrowser::wheelEvent(pEvent);
579}
580
581void UIHelpViewer::mousePressEvent(QMouseEvent *pEvent)
582{
583 if (m_fImageOverlay)
584 clearOverlay();
585
586 QIWithRetranslateUI<QTextBrowser>::mousePressEvent(pEvent);
587
588 QString strAnchor = anchorAt(pEvent->pos());
589 if (!strAnchor.isEmpty())
590 {
591 if (pEvent->modifiers() & Qt::ControlModifier)
592 {
593 QString strLink = source().resolved(strAnchor).toString();
594 emit sigOpenLinkInNewTab(strLink, true);
595 return;
596 }
597 }
598
599 loadImageAtPosition(pEvent->globalPos());
600}
601
602void UIHelpViewer::mouseMoveEvent(QMouseEvent *pEvent)
603{
604 if (m_fImageOverlay)
605 return;
606
607 QIWithRetranslateUI<QTextBrowser>::mouseMoveEvent(pEvent);
608
609 QPoint viewportCoordinates = viewport()->mapFromGlobal(pEvent->globalPos());
610 QTextCursor cursor = cursorForPosition(viewportCoordinates);
611 if (cursor.charFormat().isImageFormat())
612 viewport()->setCursor(m_handCursor);
613 else
614 viewport()->setCursor(m_defaultCursor);
615}
616
617void UIHelpViewer::mouseDoubleClickEvent(QMouseEvent *pEvent)
618{
619 if (m_fImageOverlay)
620 clearOverlay();
621
622 QIWithRetranslateUI<QTextBrowser>::mouseDoubleClickEvent(pEvent);
623}
624
625void UIHelpViewer::paintEvent(QPaintEvent *pEvent)
626{
627 QIWithRetranslateUI<QTextBrowser>::paintEvent(pEvent);
628 // if (m_pOverlayBlurEffect && m_pOverlayBlurEffect->isEnabled())
629 // m_pOverlayBlurEffect->setEnabled(false);
630
631 if (m_pOverlayLabel && m_pOverlayWidget)
632 {
633 if (m_fImageOverlay)
634 {
635
636 // QPainter painter(viewport());
637 // painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
638 QSize size(0.8 * width(), 0.8 * height());
639 m_pOverlayLabel->setPixmap(m_overlayPixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation));
640 m_pOverlayWidget->move(0.1 * width(), 0.1 * height());
641 m_pOverlayWidget->show();
642 // QRect rect(10, 10, 0.4 * width() - 20, height() - 20);
643 // painter.drawPixmap(rect, m_overlayPixmap);
644 //painter.fillRect(rect, Qt::red);
645 }
646 else
647 m_pOverlayWidget->hide();
648
649 }
650}
651
652void UIHelpViewer::retranslateUi()
653{
654}
655
656void UIHelpViewer::moveFindWidgetIn(int iMargin)
657{
658 if (!m_pFindInPageWidget)
659 return;
660
661 QRect rect = m_pFindInPageWidget->geometry();
662 if (rect.left() < iMargin)
663 rect.translate(-rect.left() + iMargin, 0);
664 if (rect.right() > width() - iMargin)
665 rect.translate((width() - iMargin - rect.right()), 0);
666 if (rect.top() < iMargin)
667 rect.translate(0, -rect.top() + iMargin);
668
669 if (rect.bottom() > height() - iMargin)
670 rect.translate(0, (height() - iMargin - rect.bottom()));
671 m_pFindInPageWidget->setGeometry(rect);
672 m_pFindInPageWidget->update();
673}
674
675bool UIHelpViewer::isRectInside(const QRect &rect, int iMargin) const
676{
677 if (rect.left() < iMargin || rect.top() < iMargin)
678 return false;
679 if (rect.right() > width() - iMargin || rect.bottom() > height() - iMargin)
680 return false;
681 return true;
682}
683
684void UIHelpViewer::findAllMatches(const QString &searchString)
685{
686 QTextDocument *pDocument = document();
687 AssertReturnVoid(pDocument);
688
689 m_matchedCursorPosition.clear();
690 if (searchString.isEmpty())
691 return;
692 QTextCursor cursor(pDocument);
693 QTextDocument::FindFlags flags;
694 int iMatchCount = 0;
695 while (!cursor.isNull() && !cursor.atEnd())
696 {
697 cursor = pDocument->find(searchString, cursor, flags);
698 if (!cursor.isNull())
699 {
700 m_matchedCursorPosition << cursor.position() - searchString.length();
701 ++iMatchCount;
702 }
703 }
704}
705
706void UIHelpViewer::highlightFinds(int iSearchTermLength)
707{
708 QTextDocument* pDocument = document();
709 AssertReturnVoid(pDocument);
710 /* Clear previous highlight: */
711 pDocument->undo();
712
713 QTextCursor highlightCursor(pDocument);
714 QTextCursor cursor(pDocument);
715 cursor.beginEditBlock();
716 for (int i = 0; i < m_matchedCursorPosition.size(); ++i)
717 {
718 highlightCursor.setPosition(m_matchedCursorPosition[i]);
719
720 QTextCharFormat colorFormat(highlightCursor.charFormat());
721 colorFormat.setBackground(Qt::yellow);
722
723 highlightCursor.setPosition(m_matchedCursorPosition[i] + iSearchTermLength, QTextCursor::KeepAnchor);
724 if (!highlightCursor.isNull())
725 highlightCursor.setCharFormat(colorFormat);
726 }
727 cursor.endEditBlock();
728}
729
730void UIHelpViewer::selectMatch(int iMatchIndex, int iSearchStringLength)
731{
732 QTextCursor cursor = textCursor();
733 /* Move the cursor to the beginning of the matched string: */
734 cursor.setPosition(m_matchedCursorPosition.at(iMatchIndex), QTextCursor::MoveAnchor);
735 /* Move the cursor to the end of the matched string while keeping the anchor at the begining thus selecting the text: */
736 cursor.setPosition(m_matchedCursorPosition.at(iMatchIndex) + iSearchStringLength, QTextCursor::KeepAnchor);
737 ensureCursorVisible();
738 setTextCursor(cursor);
739}
740
741void UIHelpViewer::sltHandleOpenLinkInNewTab()
742{
743 QAction *pSender = qobject_cast<QAction*>(sender());
744 if (!pSender)
745 return;
746 QUrl url = pSender->data().toUrl();
747 if (url.isValid())
748 emit sigOpenLinkInNewTab(url, false);
749}
750
751void UIHelpViewer::sltHandleOpenLink()
752{
753 QAction *pSender = qobject_cast<QAction*>(sender());
754 if (!pSender)
755 return;
756 QUrl url = pSender->data().toUrl();
757 if (url.isValid())
758 QTextBrowser::setSource(url);
759}
760
761void UIHelpViewer::sltHandleCopyLink()
762{
763 QAction *pSender = qobject_cast<QAction*>(sender());
764 if (!pSender)
765 return;
766 QUrl url = pSender->data().toUrl();
767 if (url.isValid())
768 {
769 QClipboard *pClipboard = QApplication::clipboard();
770 if (pClipboard)
771 pClipboard->setText(url.toString());
772 }
773}
774
775void UIHelpViewer::sltHandleFindWidgetDrag(const QPoint &delta)
776{
777 if (!m_pFindInPageWidget)
778 return;
779 QRect geo = m_pFindInPageWidget->geometry();
780 geo.translate(delta);
781
782 /* Allow the move if m_pFindInPageWidget stays inside after the move: */
783 if (isRectInside(geo, m_iMarginForFindWidget))
784 m_pFindInPageWidget->move(m_pFindInPageWidget->pos() + delta);
785 m_fFindWidgetDragged = true;
786 update();
787}
788
789void UIHelpViewer::sltHandleFindInPageSearchTextChange(const QString &strSearchText)
790{
791 m_iSearchTermLength = strSearchText.length();
792 findAllMatches(strSearchText);
793 highlightFinds(m_iSearchTermLength);
794 //scrollToMatch(int iMatchIndex);
795 selectMatch(0, m_iSearchTermLength);
796 if (m_pFindInPageWidget)
797 m_pFindInPageWidget->setMatchCountAndCurrentIndex(m_matchedCursorPosition.size(), 0);
798}
799
800void UIHelpViewer::sltSelectPreviousMatch()
801{
802 m_iSelectedMatchIndex = m_iSelectedMatchIndex <= 0 ? m_matchedCursorPosition.size() - 1 : (m_iSelectedMatchIndex - 1);
803 selectMatch(m_iSelectedMatchIndex, m_iSearchTermLength);
804 if (m_pFindInPageWidget)
805 m_pFindInPageWidget->setMatchCountAndCurrentIndex(m_matchedCursorPosition.size(), m_iSelectedMatchIndex);
806}
807
808void UIHelpViewer::sltSelectNextMatch()
809{
810 m_iSelectedMatchIndex = m_iSelectedMatchIndex >= m_matchedCursorPosition.size() - 1 ? 0 : (m_iSelectedMatchIndex + 1);
811 selectMatch(m_iSelectedMatchIndex, m_iSearchTermLength);
812 if (m_pFindInPageWidget)
813 m_pFindInPageWidget->setMatchCountAndCurrentIndex(m_matchedCursorPosition.size(), m_iSelectedMatchIndex);
814}
815
816void UIHelpViewer::iterateDocumentImages()
817{
818 m_imageMap.clear();
819 QTextCursor cursor = textCursor();
820 cursor.movePosition(QTextCursor::Start);
821 while (!cursor.atEnd())
822 {
823 cursor.movePosition(QTextCursor::NextCharacter);
824 if (cursor.charFormat().isImageFormat())
825 {
826 DocumentImage image;
827 QTextImageFormat imageFormat = cursor.charFormat().toImageFormat();
828 image.m_fInitialWidth = imageFormat.width();
829 image.m_iPosition = cursor.position();
830 m_imageMap[imageFormat.name()] = image;
831 }
832 }
833}
834
835void UIHelpViewer::scaleFont()
836{
837 QFont mFont = font();
838 mFont.setPointSize(m_iInitialFontPointSize * m_iZoomPercentage / 100.);
839 setFont(mFont);
840}
841
842void UIHelpViewer::scaleImages()
843{
844 for (QMap<QString, DocumentImage>::iterator iterator = m_imageMap.begin();
845 iterator != m_imageMap.end(); ++iterator)
846 {
847 QTextCursor cursor = textCursor();
848 cursor.movePosition(QTextCursor::Start);
849 cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, (*iterator).m_iPosition - 1);
850 if (cursor.isNull())
851 continue;
852 QTextCharFormat format = cursor.charFormat();
853 if (!format.isImageFormat())
854 continue;
855 QTextImageFormat imageFormat = format.toImageFormat();
856 imageFormat.setWidth((*iterator).m_fInitialWidth * m_iZoomPercentage / 100.);
857 cursor.deleteChar();
858 cursor.insertImage(imageFormat);
859 }
860}
861
862void UIHelpViewer::clearOverlay()
863{
864 m_overlayPixmap = QPixmap();
865 m_fImageOverlay = false;
866 if (m_pOverlayBlurEffect)
867 m_pOverlayBlurEffect->setEnabled(false);
868}
869
870void UIHelpViewer::loadImageAtPosition(const QPoint &globalPosition)
871{
872 clearOverlay();
873 QPoint viewportCoordinates = viewport()->mapFromGlobal(globalPosition);
874 QTextCursor cursor = cursorForPosition(viewportCoordinates);
875 if (!cursor.charFormat().isImageFormat())
876 return;
877
878 QTextImageFormat imageFormat = cursor.charFormat().toImageFormat();
879 QUrl imageFileUrl;
880 foreach (const QUrl &fileUrl, m_helpFileList)
881 {
882 if (fileUrl.toString().contains(imageFormat.name(), Qt::CaseInsensitive))
883 {
884 imageFileUrl = fileUrl;
885 break;
886 }
887 }
888
889 if (!imageFileUrl.isValid())
890 return;
891 QByteArray fileData = m_pHelpEngine->fileData(imageFileUrl);
892 if (!fileData.isEmpty())
893 {
894 m_overlayPixmap.loadFromData(fileData,"PNG");
895 if (!m_overlayPixmap.isNull())
896 {
897 m_fImageOverlay = true;
898 if (m_pOverlayBlurEffect)
899 m_pOverlayBlurEffect->setEnabled(true);
900 viewport()->setCursor(m_defaultCursor);
901 }
902 }
903}
904
905#include "UIHelpViewer.moc"
906
907#endif /* #ifdef VBOX_WITH_QHELP_VIEWER */
Note: See TracBrowser for help on using the repository browser.

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