File indexing completed on 2025-01-05 03:59:26

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