File indexing completed on 2024-04-14 03:48:01

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2012 Torsten Rahn <tackat@kde.org>
0004 // SPDX-FileCopyrightText: 2013 Mohammed Nafees <nafees.technocool@gmail.com>
0005 // SPDX-FileCopyrightText: 2012 Dennis Nienhüser <nienhueser@kde.org>
0006 // SPDX-FileCopyrightText: 2012 Illya Kovalevskyy <illya.kovalevskyy@gmail.com>
0007 //
0008 
0009 #include "PopupItem.h"
0010 #include "MarbleWidget.h"
0011 
0012 #ifdef MARBLE_NO_WEBKITWIDGETS
0013 #include "NullMarbleWebView.h"
0014 #else
0015 #include <QWebEngineView>
0016 #include <QWebEngineHistory>
0017 #include <QWebEngineSettings>
0018 #include "MarbleWebView.h"
0019 #endif
0020 
0021 #include <QDebug>
0022 #include <QPointer>
0023 #include <QPrinter>
0024 #include <QPrintDialog>
0025 #include <QMouseEvent>
0026 #include <QApplication>
0027 #include <QDesktopServices>
0028 #include <QPixmapCache>
0029 #include <qdrawutil.h>
0030 #include <QPainter>
0031 
0032 namespace Marble
0033 {
0034 
0035 PopupItem::PopupItem( QObject* parent ) :
0036     QObject( parent ),
0037     BillboardGraphicsItem(),
0038     m_widget( new QWidget() ),
0039     m_textColor( QColor(Qt::black) ),
0040     m_backColor( QColor(Qt::white) ),
0041     m_needMouseRelease(false)
0042 {
0043 //    setCacheMode( ItemCoordinateCache );
0044     setVisible( false );
0045     setSize( QSizeF( 300.0, 320.0 ) );
0046 
0047     m_ui.setupUi( m_widget );
0048     m_ui.goBackButton->setVisible( false );
0049     connect( m_ui.goBackButton, SIGNAL(clicked()), this, SLOT(goBack()) );
0050 
0051 #ifdef QT_NO_PRINTER
0052     m_ui.printButton->setVisible( false );
0053 #else
0054     m_ui.printButton->setVisible( true );
0055     connect( m_ui.printButton, SIGNAL(clicked()), this, SLOT(printContent()) );
0056 #endif
0057 
0058     m_widget->setVisible(true);
0059     m_widget->setAttribute(Qt::WA_DontShowOnScreen);
0060     m_widget->setAttribute( Qt::WA_NoSystemBackground, true );
0061     m_widget->setAttribute(Qt::WA_QuitOnClose, false);
0062     QPalette palette = m_ui.webView->palette();
0063     palette.setBrush(QPalette::Base, Qt::transparent);
0064 #ifndef MARBLE_NO_WEBKITWIDGETS
0065     m_ui.webView->setPalette(palette);
0066     m_ui.webView->page()->settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls, true);
0067     m_ui.webView->page()->settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);
0068 #endif
0069     m_ui.webView->setAttribute(Qt::WA_OpaquePaintEvent, false);
0070     m_ui.webView->setUrl( QUrl( "about:blank" ) );
0071 
0072     connect( m_ui.hideButton, SIGNAL(clicked()), this, SIGNAL(hide()) );
0073 
0074 #ifndef MARBLE_NO_WEBKITWIDGETS
0075     connect( m_ui.webView->page(), SIGNAL(titleChanged(QString)), m_ui.titleText, SLOT(setText(QString)) );
0076     connect( m_ui.webView->page(), SIGNAL(urlChanged(QUrl)), this, SLOT(updateBackButton()) );
0077     // Update the popupitem on changes while loading the webpage
0078     connect(m_ui.webView, SIGNAL(loadFinished(bool)), this, SLOT(requestUpdate()));
0079 #endif
0080 }
0081 
0082 PopupItem::~PopupItem()
0083 {
0084     delete m_widget;
0085 }
0086 
0087 bool PopupItem::isPrintButtonVisible() const
0088 {
0089     return m_ui.printButton->isVisible();
0090 }
0091 
0092 void PopupItem::setPrintButtonVisible( bool display )
0093 {
0094     m_ui.printButton->setVisible( display );
0095 }
0096 
0097 void PopupItem::setUrl( const QUrl &url )
0098 {
0099     m_ui.webView->setUrl( url );
0100 
0101     QPalette palette = m_ui.webView->palette();
0102     palette.setBrush(QPalette::Base, Qt::transparent);
0103 #ifndef MARBLE_NO_WEBKITWIDGETS
0104     m_ui.webView->setPalette(palette);
0105 #endif
0106     m_ui.webView->setAttribute(Qt::WA_OpaquePaintEvent, false);
0107 
0108     requestUpdate();
0109 }
0110 
0111 void PopupItem::setContent( const QString &html, const QUrl &baseUrl )
0112 {
0113     m_content = html;
0114     m_baseUrl = baseUrl;
0115 #ifndef MARBLE_NO_WEBKITWIDGETS
0116     m_ui.webView->setHtml( html, baseUrl );
0117 #endif
0118 
0119     requestUpdate();
0120 }
0121 
0122 void PopupItem::setTextColor(const QColor &color)
0123 {
0124     if(color.isValid() && m_ui.titleText != nullptr) {
0125         m_textColor = color;
0126         QPalette palette(m_ui.titleText->palette());
0127         palette.setColor(QPalette::WindowText, m_textColor);
0128         m_ui.titleText->setPalette(palette);
0129 
0130         requestUpdate();
0131     }
0132 }
0133 
0134 void PopupItem::setBackgroundColor(const QColor &color)
0135 {
0136     if(color.isValid()) {
0137         m_backColor = color;
0138         QPixmapCache::remove( "marble/webpopup/webpopup2" );
0139         QPixmapCache::remove( "marble/webpopup/arrow2_topleft" );
0140         QPixmapCache::remove( "marble/webpopup/arrow2_bottomleft" );
0141         QPixmapCache::remove( "marble/webpopup/arrow2_topright" );
0142         QPixmapCache::remove( "marble/webpopup/arrow2_bottomright" );
0143 
0144         requestUpdate();
0145     }
0146 }
0147 
0148 void PopupItem::colorize( QImage &img, const QColor &col )
0149 {
0150     if (img.depth() <= 8) return;
0151     int pixels = img.width()*img.height();
0152     unsigned int *data = (unsigned int *) img.bits();
0153     for (int i=0; i < pixels; ++i) {
0154         int val = qGray(data[i]);
0155         data[i] = qRgba(col.red()*val/255,col.green()*val/255, col.blue()*val/255, qAlpha(data[i]));
0156     }
0157 }
0158 
0159 void PopupItem::paint( QPainter *painter )
0160 {
0161     QRect popupRect;
0162     QPixmap image = pixmap("marble/webpopup/arrow2_vertical_topright");
0163 
0164     if ( alignment() & Qt::AlignRight ) {
0165         popupRect.setRect( image.width() - 13, -10,
0166                            size().width() - ( image.width() - 3 ),
0167                            size().height()  );
0168         qDrawBorderPixmap(painter, popupRect, QMargins( 20, 20, 20, 20 ),
0169                           pixmap("marble/webpopup/webpopup2"));
0170         if ( alignment() & Qt::AlignTop ) {
0171             image = pixmap("marble/webpopup/arrow2_bottomleft");
0172             painter->drawPixmap( 0, size().height() - image.height(), image );
0173         } else if ( alignment() & Qt::AlignBottom ) {
0174             image = pixmap("marble/webpopup/arrow2_topleft");
0175             painter->drawPixmap( 0, 0, image );
0176         } else { // for no horizontal align value and Qt::AlignVCenter
0177             image = pixmap("marble/webpopup/arrow2_topleft");
0178             painter->drawPixmap( 0, size().height() / 2, image );
0179         }
0180         m_widget->render( painter, QPoint( image.width() - 3, 0 ) );
0181     } else if ( alignment() & Qt::AlignLeft ) {
0182         popupRect.setRect( -10, -10,
0183                            size().width() - ( image.width() - 3 ),
0184                            size().height() );
0185         qDrawBorderPixmap(painter, popupRect, QMargins( 20, 20, 20, 20 ),
0186                           pixmap("marble/webpopup/webpopup2"));
0187         if ( alignment() & Qt::AlignTop ) {
0188             image = pixmap("marble/webpopup/arrow2_bottomright");
0189             painter->drawPixmap( size().width() - image.width(),
0190                                  size().height() - image.height(), image );
0191         } else if ( alignment() & Qt::AlignBottom ) {
0192             image = pixmap("marble/webpopup/arrow2_topright");
0193             painter->drawPixmap( size().width() - image.width(),
0194                                  0, image );
0195         } else { // for no horizontal align value and Qt::AlignVCenter
0196             image = pixmap("marble/webpopup/arrow2_topright");
0197             painter->drawPixmap( size().width() - image.width(),
0198                                  size().height() / 2 - image.height() / 2 + 23, image );
0199         }
0200         m_widget->render( painter, QPoint( 5, 0 ), QRegion() );
0201     } else if ( alignment() & Qt::AlignHCenter )
0202     {
0203         if ( alignment() & Qt::AlignTop )
0204         {
0205             image = pixmap("marble/webpopup/arrow2_vertical_bottomright");
0206             popupRect.setRect( -10, -10, size().width(),
0207                                size().height() - image.height() + 3 );
0208             qDrawBorderPixmap(painter, popupRect, QMargins( 20, 20, 20, 20 ),
0209                               pixmap("marble/webpopup/webpopup2"));
0210             painter->drawPixmap( size().width() / 2 - image.width(),
0211                                  size().height() - image.height(), image );
0212             m_widget->render( painter, QPoint( 0, 0 ), QRegion() );
0213         } else if ( alignment() & Qt::AlignBottom ) {
0214             image = pixmap("marble/webpopup/arrow2_vertical_topleft");
0215             popupRect.setRect( -10, image.height() - 13, size().width(),
0216                                size().height() - image.height() + 3 );
0217             qDrawBorderPixmap(painter, popupRect, QMargins( 20, 20, 20, 20 ),
0218                               pixmap("marble/webpopup/webpopup2"));
0219             painter->drawPixmap( size().width() / 2, 0, image );
0220             m_widget->render( painter, QPoint( 5, image.height() - 7 ), QRegion() );
0221         } else { // for no horizontal align value and Qt::AlignVCenter
0222             popupRect.setRect( -10, -10, size().width(),
0223                                size().height());
0224             qDrawBorderPixmap(painter, popupRect, QMargins( 20, 20, 20, 20 ),
0225                               pixmap("marble/webpopup/webpopup2"));
0226             m_widget->render( painter, QPoint( 0, 0 ), QRegion() );
0227         }
0228     }
0229     m_widget->setFixedSize( popupRect.width() - 20,
0230                             popupRect.height() - 20 );
0231 }
0232 
0233 bool PopupItem::eventFilter( QObject *object, QEvent *e )
0234 {
0235     MarbleWidget *widget = dynamic_cast<MarbleWidget*> ( object );
0236     if ( !widget ) {
0237         return BillboardGraphicsItem::eventFilter( object, e );
0238     }
0239 
0240     if ( e->type() == QEvent::ContextMenu) {
0241         QApplication::sendEvent( m_ui.webView, e );
0242         return BillboardGraphicsItem::eventFilter( object, e );
0243     }
0244 
0245     if ( e->type() == QEvent::KeyPress ) {
0246         QApplication::sendEvent( m_ui.webView, e );
0247         return BillboardGraphicsItem::eventFilter( object, e );
0248     }
0249 
0250     if ( e->type() == QEvent::MouseButtonDblClick
0251             || e->type() == QEvent::MouseMove
0252             || e->type() == QEvent::MouseButtonPress
0253             || e->type() == QEvent::MouseButtonRelease )
0254     {
0255         // Mouse events are forwarded to the underlying widget
0256         QMouseEvent *event = static_cast<QMouseEvent*> ( e );
0257         QPoint shiftedPos = event->pos();
0258         QWidget* child = transform( shiftedPos );
0259         bool const forcedMouseRelease = m_needMouseRelease && e->type() == QEvent::MouseButtonRelease;
0260         if ( child || forcedMouseRelease ) {
0261             if ( !m_needMouseRelease && e->type() == QEvent::MouseButtonPress ) {
0262                 m_needMouseRelease = true;
0263             } else if ( forcedMouseRelease ) {
0264                 m_needMouseRelease = false;
0265             }
0266             if ( !child ) {
0267                 child = m_ui.webView;
0268             }
0269             QMouseEvent shiftedEvent = QMouseEvent( e->type(), shiftedPos,
0270                                                     event->globalPos(), event->button(), event->buttons(),
0271                                                     event->modifiers() );
0272             if ( QApplication::sendEvent( child, &shiftedEvent ) ) {
0273                 widget->setCursor( child->cursor() );
0274                 emit repaintNeeded();
0275                 return true;
0276             }
0277         }
0278     } else if ( e->type() == QEvent::Wheel ) {
0279         // Wheel events are forwarded to the underlying widget
0280         QWheelEvent *event = static_cast<QWheelEvent*> ( e );
0281         QPoint shiftedPos = event->pos();
0282         QWidget* child = transform( shiftedPos );
0283         if ( child ) {
0284             QWheelEvent shiftedEvent = QWheelEvent( shiftedPos,
0285                                                     event->globalPos(), event->delta(), event->buttons(),
0286                                                     event->modifiers() );
0287             if ( QApplication::sendEvent( child, &shiftedEvent ) ) {
0288                 widget->setCursor( child->cursor() );
0289                 emit repaintNeeded();
0290                 return true;
0291             }
0292         }
0293     }
0294 
0295     return BillboardGraphicsItem::eventFilter( object, e );
0296 }
0297 
0298 QWidget* PopupItem::transform( QPoint &point ) const
0299 {
0300     /*
0301      * Fixes for mouse events to trigger when the web popup
0302      * is shifted in accordance with the horizontal alignment
0303      */
0304     if ( alignment() & Qt::AlignRight )
0305         point -= QPoint( 117, 0 );
0306     else if ( alignment() & Qt::AlignLeft )
0307         point -= QPoint( 5, 0 );
0308     else if ( alignment() & Qt::AlignHCenter )
0309     {
0310         if ( alignment() & Qt::AlignTop )
0311         {
0312             point -= QPoint( 0, 0 );
0313         } else if ( alignment() & Qt::AlignBottom )
0314         {
0315             point-= QPoint( 5, 57 );
0316         } else {
0317             point -= QPoint( 0, 0 );
0318         }
0319     }
0320 
0321     const QVector<QPointF> widgetPositions = positions();
0322     QVector<QPointF>::const_iterator it = widgetPositions.constBegin();
0323     for( ; it != widgetPositions.constEnd(); ++it ) {
0324         if ( QRectF( *it, size() ).contains( point ) ) {
0325             point -= it->toPoint();
0326             QWidget* child = m_widget->childAt( point );
0327             if ( child ) {
0328                 point -= child->pos();
0329             }
0330             return child;
0331         }
0332     }
0333     return nullptr;
0334 }
0335 
0336 void PopupItem::clearHistory()
0337 {
0338     m_content.clear();
0339     m_ui.webView->setUrl( QUrl( "about:blank" ) );
0340 #ifndef MARBLE_NO_WEBKITWIDGETS
0341     m_ui.webView->history()->clear();
0342 #endif
0343 }
0344 
0345 void PopupItem::requestUpdate()
0346 {
0347     update();
0348     emit repaintNeeded();
0349 }
0350 
0351 void PopupItem::printContent() const
0352 {
0353 #ifndef QT_NO_PRINTER
0354 #ifndef MARBLE_NO_WEBKITWIDGETS
0355     QPrinter printer;
0356     QPointer<QPrintDialog> dialog = new QPrintDialog(&printer);
0357     if (dialog->exec() == QPrintDialog::Accepted) {
0358         m_ui.webView->page()->print(&printer, [=](bool){});
0359     }
0360     delete dialog;
0361 #endif
0362 #endif
0363 }
0364 
0365 void PopupItem::updateBackButton()
0366 {
0367 #ifndef MARBLE_NO_WEBKITWIDGETS
0368     bool const hasHistory = m_ui.webView->page()->history()->count() > 1;
0369     bool const previousIsHtml = !m_content.isEmpty() && m_ui.webView->page()->history()->currentItemIndex() == 1;
0370     bool const atStart = m_ui.webView->page()->history()->currentItemIndex() <= 1;
0371     bool const currentIsHtml = m_ui.webView->page()->url() == QUrl( "about:blank" );
0372 
0373     m_ui.goBackButton->setVisible( hasHistory && !currentIsHtml && ( previousIsHtml || !atStart ) );
0374 #endif
0375 }
0376 
0377 void PopupItem::goBack()
0378 {
0379 #ifndef MARBLE_NO_WEBKITWIDGETS
0380     if ( m_ui.webView->page()->history()->currentItemIndex() == 1 && !m_content.isEmpty() ) {
0381       m_ui.webView->page()->setHtml( m_content, m_baseUrl );
0382     } else {
0383         m_ui.webView->back();
0384     }
0385     updateBackButton();
0386 #endif
0387 }
0388 
0389 void PopupItem::openUrl(const QUrl &url)
0390 {
0391     QDesktopServices::openUrl(url);
0392 }
0393 
0394 QPixmap PopupItem::pixmap( const QString &imageId ) const
0395 {
0396   QPixmap result;
0397   if ( !QPixmapCache::find( imageId, &result ) ) {
0398     QImage bottom = QImage(QLatin1String(":/") + imageId + QLatin1String("_shadow.png"));
0399     QImage top =    QImage(QLatin1String(":/") + imageId + QLatin1String(".png"));
0400     colorize( top, m_backColor );
0401     QPainter painter( &bottom );
0402     painter.drawImage( QPoint(0,0), top );
0403 
0404     result = QPixmap::fromImage( bottom );
0405     QPixmapCache::insert( imageId, result );
0406   }
0407 
0408   return result;
0409 }
0410 
0411 }
0412 
0413 #include "moc_PopupItem.cpp"