File indexing completed on 2024-04-28 04:18:53
0001 /* 0002 Gwenview: an image viewer 0003 Copyright 2007 Aurélien Gâteau <agateau@kde.org> 0004 0005 This program is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU General Public License 0007 as published by the Free Software Foundation; either version 2 0008 of the License, or (at your option) any later version. 0009 0010 This program is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 GNU General Public License for more details. 0014 0015 You should have received a copy of the GNU General Public License 0016 along with this program; if not, write to the Free Software 0017 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0018 0019 */ 0020 #include "slideshow.h" 0021 0022 // STL 0023 #include <algorithm> 0024 #include <ctime> 0025 #include <random> 0026 0027 // Qt 0028 #include <QAction> 0029 #include <QTimer> 0030 0031 // KF 0032 #include <KLocalizedString> 0033 0034 // Local 0035 #include "gwenview_lib_debug.h" 0036 #include <gwenviewconfig.h> 0037 #include <lib/gvdebug.h> 0038 0039 namespace Gwenview 0040 { 0041 #undef ENABLE_LOG 0042 #undef LOG 0043 // #define ENABLE_LOG 0044 #ifdef ENABLE_LOG 0045 #define LOG(x) qCDebug(GWENVIEW_LIB_LOG) << x 0046 #else 0047 #define LOG(x) ; 0048 #endif 0049 0050 enum State { 0051 Paused, 0052 Started, 0053 WaitForEndOfUrl, 0054 }; 0055 0056 struct SlideShowPrivate { 0057 QTimer *mTimer = nullptr; 0058 State mState; 0059 QVector<QUrl> mUrls; 0060 QVector<QUrl> mShuffledUrls; 0061 QVector<QUrl>::ConstIterator mStartIt; 0062 QUrl mCurrentUrl; 0063 QUrl mLastShuffledUrl; 0064 0065 QAction *mLoopAction = nullptr; 0066 QAction *mRandomAction = nullptr; 0067 0068 QUrl findNextUrl() 0069 { 0070 if (GwenviewConfig::random()) { 0071 return findNextRandomUrl(); 0072 } else { 0073 return findNextOrderedUrl(); 0074 } 0075 } 0076 0077 QUrl findNextOrderedUrl() 0078 { 0079 QVector<QUrl>::ConstIterator it = std::find(mUrls.constBegin(), mUrls.constEnd(), mCurrentUrl); 0080 GV_RETURN_VALUE_IF_FAIL2(it != mUrls.constEnd(), QUrl(), "Current url not found in list."); 0081 0082 ++it; 0083 if (GwenviewConfig::loop()) { 0084 // Looping, if we reach the end, start again 0085 if (it == mUrls.constEnd()) { 0086 it = mUrls.constBegin(); 0087 } 0088 } else { 0089 // Not looping, have we reached the end? 0090 // FIXME: stopAtEnd 0091 if (/*(it==mUrls.end() && GwenviewConfig::stopAtEnd()) ||*/ it == mStartIt) { 0092 it = mUrls.constEnd(); 0093 } 0094 } 0095 0096 if (it != mUrls.constEnd()) { 0097 return *it; 0098 } else { 0099 return {}; 0100 } 0101 } 0102 0103 void initShuffledUrls() 0104 { 0105 mShuffledUrls = mUrls; 0106 std::random_device rd; 0107 std::mt19937 generator(rd()); 0108 std::shuffle(mShuffledUrls.begin(), mShuffledUrls.end(), generator); 0109 // Ensure the first url is different from the previous last one, so that 0110 // last url does not stay visible twice longer than usual 0111 if (mLastShuffledUrl == mShuffledUrls.first() && mShuffledUrls.count() > 1) { 0112 std::swap(mShuffledUrls[0], mShuffledUrls[1]); 0113 } 0114 mLastShuffledUrl = mShuffledUrls.last(); 0115 } 0116 0117 QUrl findNextRandomUrl() 0118 { 0119 if (mShuffledUrls.empty()) { 0120 if (GwenviewConfig::loop()) { 0121 initShuffledUrls(); 0122 } else { 0123 return {}; 0124 } 0125 } 0126 0127 const QUrl url = mShuffledUrls.last(); 0128 mShuffledUrls.pop_back(); 0129 0130 return url; 0131 } 0132 0133 void updateTimerInterval() 0134 { 0135 mTimer->setInterval(int(GwenviewConfig::interval() * 1000)); 0136 } 0137 0138 void doStart() 0139 { 0140 if (MimeTypeUtils::urlKind(mCurrentUrl) == MimeTypeUtils::KIND_VIDEO) { 0141 LOG("mState = WaitForEndOfUrl"); 0142 // Just in case 0143 mTimer->stop(); 0144 mState = WaitForEndOfUrl; 0145 } else { 0146 LOG("mState = Started"); 0147 mTimer->start(); 0148 mState = Started; 0149 } 0150 } 0151 }; 0152 0153 SlideShow::SlideShow(QObject *parent) 0154 : QObject(parent) 0155 , d(new SlideShowPrivate) 0156 { 0157 d->mState = Paused; 0158 0159 d->mTimer = new QTimer(this); 0160 connect(d->mTimer, &QTimer::timeout, this, &SlideShow::goToNextUrl); 0161 0162 d->mLoopAction = new QAction(this); 0163 d->mLoopAction->setText(i18nc("@item:inmenu toggle loop in slideshow", "Loop")); 0164 d->mLoopAction->setCheckable(true); 0165 connect(d->mLoopAction, &QAction::triggered, this, &SlideShow::updateConfig); 0166 0167 d->mRandomAction = new QAction(this); 0168 d->mRandomAction->setText(i18nc("@item:inmenu toggle random order in slideshow", "Random")); 0169 d->mRandomAction->setCheckable(true); 0170 connect(d->mRandomAction, &QAction::toggled, this, &SlideShow::slotRandomActionToggled); 0171 connect(d->mRandomAction, &QAction::triggered, this, &SlideShow::updateConfig); 0172 0173 d->mLoopAction->setChecked(GwenviewConfig::loop()); 0174 d->mRandomAction->setChecked(GwenviewConfig::random()); 0175 } 0176 0177 SlideShow::~SlideShow() 0178 { 0179 GwenviewConfig::self()->save(); 0180 delete d; 0181 } 0182 0183 QAction *SlideShow::loopAction() const 0184 { 0185 return d->mLoopAction; 0186 } 0187 0188 QAction *SlideShow::randomAction() const 0189 { 0190 return d->mRandomAction; 0191 } 0192 0193 void SlideShow::start(const QList<QUrl> &urls) 0194 { 0195 d->mUrls.resize(urls.size()); 0196 std::copy(urls.begin(), urls.end(), d->mUrls.begin()); 0197 0198 d->mStartIt = std::find(d->mUrls.constBegin(), d->mUrls.constEnd(), d->mCurrentUrl); 0199 if (d->mStartIt == d->mUrls.constEnd()) { 0200 qCWarning(GWENVIEW_LIB_LOG) << "Current url not found in list, aborting.\n"; 0201 return; 0202 } 0203 0204 if (GwenviewConfig::random()) { 0205 d->initShuffledUrls(); 0206 } 0207 0208 d->updateTimerInterval(); 0209 d->mTimer->setSingleShot(false); 0210 d->doStart(); 0211 Q_EMIT stateChanged(true); 0212 } 0213 0214 void SlideShow::setInterval(int intervalInSeconds) 0215 { 0216 GwenviewConfig::setInterval(double(intervalInSeconds)); 0217 d->updateTimerInterval(); 0218 Q_EMIT intervalChanged(intervalInSeconds); 0219 } 0220 0221 int SlideShow::interval() const 0222 { 0223 return GwenviewConfig::interval(); 0224 } 0225 0226 int SlideShow::position() const 0227 { 0228 // TODO: also support videos 0229 0230 // QTimer::remainingTime() returns -1 if inactive 0231 // and there are moments where mState == Started but timer already done but not yet next url reached 0232 // so handle that 0233 if (d->mState == Started) { 0234 if (d->mTimer->isActive()) { 0235 return interval() * 1000 - d->mTimer->remainingTime(); 0236 } 0237 // already timeout reached, but not yet progressed to next url 0238 return interval(); 0239 } 0240 0241 return 0; 0242 } 0243 0244 void SlideShow::pause() 0245 { 0246 LOG("Stopping timer"); 0247 d->mTimer->stop(); 0248 d->mState = Paused; 0249 Q_EMIT stateChanged(false); 0250 } 0251 0252 void SlideShow::resumeAndGoToNextUrl() 0253 { 0254 LOG(""); 0255 if (d->mState == WaitForEndOfUrl) { 0256 goToNextUrl(); 0257 } 0258 } 0259 0260 void SlideShow::goToNextUrl() 0261 { 0262 LOG(""); 0263 const QUrl url = d->findNextUrl(); 0264 LOG("url:" << url); 0265 if (!url.isValid()) { 0266 pause(); 0267 return; 0268 } 0269 Q_EMIT goToUrl(url); 0270 } 0271 0272 void SlideShow::setCurrentUrl(const QUrl &url) 0273 { 0274 LOG(url); 0275 if (d->mCurrentUrl == url) { 0276 return; 0277 } 0278 d->mCurrentUrl = url; 0279 // Restart timer to avoid showing new url for the remaining time of the old 0280 // url 0281 if (d->mState != Paused) { 0282 d->doStart(); 0283 } 0284 } 0285 0286 bool SlideShow::isRunning() const 0287 { 0288 return d->mState != Paused; 0289 } 0290 0291 void SlideShow::updateConfig() 0292 { 0293 GwenviewConfig::setLoop(d->mLoopAction->isChecked()); 0294 GwenviewConfig::setRandom(d->mRandomAction->isChecked()); 0295 } 0296 0297 void SlideShow::slotRandomActionToggled(bool on) 0298 { 0299 if (on && d->mState != Paused) { 0300 d->initShuffledUrls(); 0301 } 0302 } 0303 0304 } // namespace 0305 0306 #include "moc_slideshow.cpp"