File indexing completed on 2024-12-01 06:40:45

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2010 Dennis Nienhüser <nienhueser@kde.org>
0004 // SPDX-FileCopyrightText: 2010, 2011 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
0005 //
0006 
0007 #include "ProgressFloatItem.h"
0008 
0009 #include "MarbleDebug.h"
0010 #include "MarbleDirs.h"
0011 #include "MarbleModel.h"
0012 #include "MarbleWidget.h"
0013 #include "ViewportParams.h"
0014 #include "HttpDownloadManager.h"
0015 
0016 #include <QRect>
0017 #include <QColor>
0018 #include <QPaintDevice>
0019 #include <QPainter>
0020 #include <QPainterPath>
0021 #include <QThread>
0022 
0023 namespace Marble
0024 {
0025 
0026 ProgressFloatItem::ProgressFloatItem( const MarbleModel *marbleModel )
0027     : AbstractFloatItem( marbleModel, QPointF( -10.5, -150.5 ), QSizeF( 40.0, 40.0 ) ),
0028       m_isInitialized( false ),
0029       m_totalJobs( 0 ),
0030       m_completedJobs ( 0 ),
0031       m_completed( 1 ),
0032       m_progressHideTimer(),
0033       m_progressShowTimer(),
0034       m_active( false ),
0035       m_fontSize( 0 ),
0036       m_repaintTimer()
0037 {
0038     // This timer is responsible to activate the automatic display with a small delay
0039     m_progressShowTimer.setInterval( 250 );
0040     m_progressShowTimer.setSingleShot( true );
0041     connect( &m_progressShowTimer, SIGNAL(timeout()), this, SLOT(show()) );
0042 
0043     // This timer is responsible to hide the automatic display when downloads are finished
0044     m_progressHideTimer.setInterval( 750 );
0045     m_progressHideTimer.setSingleShot( true );
0046     connect( &m_progressHideTimer, SIGNAL(timeout()), this, SLOT(hideProgress()) );
0047 
0048     // Repaint timer
0049     m_repaintTimer.setSingleShot( true );
0050     m_repaintTimer.setInterval( 1000 );
0051     connect( &m_repaintTimer, SIGNAL(timeout()), this, SIGNAL(repaintNeeded()) );
0052 
0053     // Plugin is enabled by default
0054     setEnabled( true );
0055 
0056     // Plugin is visible by default on devices with small screens only
0057     setVisible( MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen );    
0058 }
0059 
0060 ProgressFloatItem::~ProgressFloatItem ()
0061 {
0062     // nothing to do
0063 }
0064 
0065 QStringList ProgressFloatItem::backendTypes() const
0066 {
0067     return QStringList(QStringLiteral("progress"));
0068 }
0069 
0070 QString ProgressFloatItem::name() const
0071 {
0072     return tr( "Download Progress Indicator" );
0073 }
0074 
0075 QString ProgressFloatItem::guiString() const
0076 {
0077     return tr( "&Download Progress" );
0078 }
0079 
0080 QString ProgressFloatItem::nameId() const
0081 {
0082     return QStringLiteral("progress");
0083 }
0084 
0085 QString ProgressFloatItem::version() const
0086 {
0087     return QStringLiteral("1.0");
0088 }
0089 
0090 QString ProgressFloatItem::description() const
0091 {
0092     return tr( "Shows a pie chart download progress indicator" );
0093 }
0094 
0095 QString ProgressFloatItem::copyrightYears() const
0096 {
0097     return QStringLiteral("2010, 2011");
0098 }
0099 
0100 QVector<PluginAuthor> ProgressFloatItem::pluginAuthors() const
0101 {
0102     return QVector<PluginAuthor>()
0103             << PluginAuthor(QStringLiteral("Dennis Nienhüser"), QStringLiteral("nienhueser@kde.org"))
0104             << PluginAuthor(QStringLiteral("Bernhard Beschow"), QStringLiteral("bbeschow@cs.tu-berlin.de"));
0105 }
0106 
0107 QIcon ProgressFloatItem::icon() const
0108 {
0109     return m_icon;
0110 }
0111 
0112 void ProgressFloatItem::initialize()
0113 {
0114     const HttpDownloadManager* manager = marbleModel()->downloadManager();
0115     Q_ASSERT( manager );
0116     connect( manager, SIGNAL(progressChanged(int,int)), this, SLOT(handleProgress(int,int)) , Qt::UniqueConnection );
0117     connect( manager, SIGNAL(jobRemoved()), this, SLOT(removeProgressItem()), Qt::UniqueConnection );
0118 
0119     // Calculate font size
0120     QFont myFont = font();
0121     const QString text = "100%";
0122     int fontSize = myFont.pointSize();
0123     while( QFontMetrics( myFont ).boundingRect( text ).width() < contentRect().width() - 2 ) {
0124         ++fontSize;
0125         myFont.setPointSize( fontSize );
0126     }
0127     m_fontSize = fontSize - 1;
0128 
0129     // The icon resembles the pie chart
0130     QImage canvas( 16, 16, QImage::Format_ARGB32 );
0131     canvas.fill( Qt::transparent );
0132     QPainter painter( &canvas );
0133     painter.setRenderHint( QPainter::Antialiasing, true );
0134     painter.setPen( QColor ( Qt::black ) );
0135     painter.drawEllipse( 1, 1, 14, 14 );
0136     painter.setPen( Qt::NoPen );
0137     painter.setBrush( QBrush( QColor( Qt::darkGray ) ) );
0138     painter.drawPie( 2, 2, 12, 12, 1440, -1325 ); // 23 percent of a full circle
0139     m_icon = QIcon( QPixmap::fromImage( canvas ) );
0140 
0141     m_isInitialized = true;
0142 }
0143 
0144 bool ProgressFloatItem::isInitialized() const
0145 {
0146     return m_isInitialized;
0147 }
0148 
0149 QPainterPath ProgressFloatItem::backgroundShape() const
0150 {
0151     QPainterPath path;
0152 
0153     if ( active() ) {
0154         // Circular shape if active, invisible otherwise
0155         QRectF rect = contentRect();
0156         qreal width = rect.width();
0157         qreal height = rect.height();
0158         path.addEllipse( marginLeft() + 2 * padding(), marginTop() + 2 * padding(), width, height );
0159     }
0160 
0161     return path;
0162 }
0163 
0164 void ProgressFloatItem::paintContent( QPainter *painter )
0165 {
0166     // Timers cannot be stopped from another thread (e.g. from QtQuick RenderThread).
0167     if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
0168         // Stop repaint timer if it is already running
0169         if (m_repaintTimer.isActive()) {
0170             m_repaintTimer.stop();
0171         }
0172     }
0173 
0174     if ( !active() ) {
0175         return;
0176     }
0177 
0178     painter->save();
0179 
0180     // Paint progress pie
0181     int startAngle =  90 * 16; // 12 o' clock
0182     int spanAngle = -ceil ( 360 * 16 * m_completed );
0183     QRectF rect( contentRect() );
0184     rect.adjust( 1, 1, -1, -1 );
0185 
0186     painter->setBrush( QColor( Qt::white ) );
0187     painter->setPen( Qt::NoPen );
0188     painter->drawPie( rect, startAngle, spanAngle );
0189 
0190     // Paint progress label
0191     QFont myFont = font();
0192     myFont.setPointSize( m_fontSize );
0193     const QString done = QString::number((int) (m_completed * 100)) + QLatin1Char('%');
0194     int fontWidth = QFontMetrics( myFont ).boundingRect( done ).width();
0195     QPointF baseline( padding() + 0.5 * ( rect.width() - fontWidth ), 0.75 * rect.height() );
0196     QPainterPath path;
0197     path.addText( baseline, myFont, done );
0198 
0199     painter->setFont( myFont );
0200     painter->setBrush( QBrush() );
0201     painter->setPen( QPen() );
0202     painter->drawPath( path );
0203 
0204     painter->restore();
0205 }
0206 
0207 void ProgressFloatItem::removeProgressItem()
0208 {
0209     m_jobMutex.lock();
0210     ++m_completedJobs;
0211     m_jobMutex.unlock();
0212 
0213     if ( enabled() ) {
0214         if ( !active() && !m_progressShowTimer.isActive() ) {
0215             m_progressShowTimer.start();
0216             m_progressHideTimer.stop();
0217         } else if ( active() ) {
0218             update();
0219             scheduleRepaint();
0220         }
0221     }
0222 }
0223 
0224 void ProgressFloatItem::handleProgress( int current, int queued )
0225 {
0226     m_jobMutex.lock();
0227     if ( current < 1 ) {
0228         m_totalJobs = 0;
0229         m_completedJobs = 0;
0230     } else {
0231         m_totalJobs = qMax<int>( m_totalJobs, queued + current );
0232     }
0233     m_jobMutex.unlock();
0234 
0235     if ( enabled() ) {
0236         if ( !active() && !m_progressShowTimer.isActive() && m_totalJobs > 0 ) {
0237             m_progressShowTimer.start();
0238             m_progressHideTimer.stop();
0239         } else if ( active() ) {
0240             if ( m_totalJobs < 1 || m_completedJobs == m_totalJobs ) {
0241                 m_progressShowTimer.stop();
0242                 m_progressHideTimer.start();
0243             }
0244             update();
0245             scheduleRepaint();
0246         }
0247 
0248         m_completed = 1.0;
0249         if ( m_totalJobs && m_completedJobs <= m_totalJobs ) {
0250             m_completed = (qreal) m_completedJobs / (qreal) m_totalJobs;
0251         }
0252     }
0253 }
0254 
0255 void ProgressFloatItem::hideProgress()
0256 {
0257     if ( enabled() ) {
0258         setActive( false );
0259 
0260         update();
0261         emit repaintNeeded( QRegion() );
0262     }
0263 }
0264 
0265 bool ProgressFloatItem::active() const
0266 {
0267     return m_active;
0268 }
0269 
0270 void ProgressFloatItem::setActive( bool active )
0271 {
0272     m_active = active;
0273     update();
0274 }
0275 
0276 void ProgressFloatItem::show()
0277 {
0278     setActive( true );
0279 
0280     update();
0281     emit repaintNeeded( QRegion() );
0282 }
0283 
0284 void ProgressFloatItem::scheduleRepaint()
0285 {
0286     if ( !m_repaintTimer.isActive() ) {
0287         m_repaintTimer.start();
0288     }
0289 }
0290 
0291 }
0292 
0293 #include "moc_ProgressFloatItem.cpp"