/* Slide Show Screen Saver * (C) 1999 Stefan Taferner * (C) 2001 Martin R. Jones * (C) 2003 Chris Howells * (C) 2003 Sven Leiber * * This code is under GPL * * 2001/03/04 Converted to libkscreensaver by Martin R. Jones. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_KEXIV2 #include #endif #include "slideshow.h" #include "moc_slideshow.cpp" #define SLIDESHOW_VERSION "2.3.0" static const char version[] = SLIDESHOW_VERSION; static const char description[] = I18N_NOOP("KSlideshow"); static KAboutData* s_aboutData = 0; // libkscreensaver interface class KSlideShowSaverInterface : public KScreenSaverInterface { public: virtual KAboutData* aboutData() { return s_aboutData; } virtual KScreenSaver* create( WId id ) { return new kSlideShowSaver( id ); } virtual QDialog* setup() { return new kSlideShowSetup(); } }; int main( int argc, char *argv[] ) { s_aboutData = new KAboutData( "kslideshow.kss", "klock", ki18n("SlideShow"), version, ki18n(description), KAboutData::License_GPL, ki18n("(c) 1999-2003, The KDE Team") ); s_aboutData->addAuthor(ki18n("Stefan Taferner"), KLocalizedString(), "taferner@kde.org"); s_aboutData->addAuthor(ki18n("Chris Howells"), KLocalizedString(), "howells@kde.org"); s_aboutData->addAuthor(ki18n("Sven Leiber"), KLocalizedString(), "s.leiber@web.de"); KSlideShowSaverInterface kss; return kScreenSaverMain( argc, argv, kss ); } //============================================================================= // Class kSlideShowSaver //============================================================================= kSlideShowSaver::kSlideShowSaver( WId id ): KScreenSaver(id) { mEffect = NULL; mNumEffects = 0; mIntArray = NULL; readConfig(); registerEffects(); initNextScreen(); mFileIdx = 0; mEffectRunning = false; mTimer.setSingleShot(true); connect(&mTimer, SIGNAL(timeout()), SLOT(update())); mTimer.start(10); QDesktopWidget *d = QApplication::desktop(); if(geometry() == d->geometry() && d->screenCount() > 1) { for(int i = 0; i < d->screenCount(); ++i) { QRect s = d->screenGeometry(i); mGeoList.append(mScreenGeo(s.width(), s.height(), s.topLeft().x(), s.topLeft().y())); } } else { mGeoList.append(mScreenGeo(width(), height(), 0, 0)); } setAttribute(Qt::WA_NoSystemBackground); createNextScreen(); show(); } //---------------------------------------------------------------------------- kSlideShowSaver::~kSlideShowSaver() { delete [] mIntArray; delete [] mEffectList; } //----------------------------------------------------------------------------- void kSlideShowSaver::initNextScreen() { int w, h; w = width(); h = height(); mNextScreen = QPixmap(w, h); } //----------------------------------------------------------------------------- void kSlideShowSaver::readConfig() { KConfigGroup config(KGlobal::config(), "Settings"); mShowRandom = config.readEntry("ShowRandom", true); mZoomImages = config.readEntry("ZoomImages", false); mPrintName = config.readEntry("PrintName", true); mPrintPath = config.readEntry("PrintPath", false); mDirectory = config.readPathEntry("Directory", KGlobal::dirs()->findDirs("wallpaper", QLatin1String( "" )).last()); mDelay = config.readEntry("Delay", 10) * 1000; mSubdirectory = config.readEntry("SubDirectory", false); mRandomPosition = config.readEntry("RandomPosition", false); mEffectsEnabled = config.readEntry("EffectsEnabled", true); loadDirectory(); } //---------------------------------------------------------------------------- void kSlideShowSaver::registerEffects() { if (!mEffectsEnabled) { mEffectList = 0; return; } int i = 0; mEffectList = new EffectMethod[64]; // mEffectList[i++] = &kSlideShowSaver::effectChessboard; mEffectList[i++] = &kSlideShowSaver::effectMultiCircleOut; mEffectList[i++] = &kSlideShowSaver::effectSpiralIn; mEffectList[i++] = &kSlideShowSaver::effectSweep; // mEffectList[i++] = &kSlideShowSaver::effectMeltdown; mEffectList[i++] = &kSlideShowSaver::effectCircleOut; mEffectList[i++] = &kSlideShowSaver::effectBlobs; // mEffectList[i++] = &kSlideShowSaver::effectHorizLines; // mEffectList[i++] = &kSlideShowSaver::effectVertLines; // mEffectList[i++] = &kSlideShowSaver::effectRandom; mEffectList[i++] = &kSlideShowSaver::effectGrowing; mEffectList[i++] = &kSlideShowSaver::effectIncomingEdges; mNumEffects = i; // mNumEffects = 1; //...for testing } //---------------------------------------------------------------------------- int kSlideShowSaver::effectMultiCircleOut(bool aInit) { int x, y, i; double alpha; static QPolygon pa(4); if (aInit) { mw = width(); mh = height(); mx = mw; my = mh>>1; pa.setPoint(0, mw>>1, mh>>1); pa.setPoint(3, mw>>1, mh>>1); mfy = sqrt((double)mw*mw + mh*mh) / 2; mi = KRandom::random()%15 + 2; mfd = M_PI*2/mi; mAlpha = mfd; mwait = 10 * mi; mfx = M_PI/32; // divisor must be powers of 8 } if (mAlpha < 0) { showNextScreen(); return -1; } QPainter p(this); QBrush brush; brush.setTexture(mNextScreen); p.setBrush(brush); p.setPen(Qt::NoPen); for (alpha=mAlpha, i=mi; i>=0; i--, alpha+=mfd) { x = (mw>>1) + (int)(mfy * cos(-alpha)); y = (mh>>1) + (int)(mfy * sin(-alpha)); mx = (mw>>1) + (int)(mfy * cos(-alpha + mfx)); my = (mh>>1) + (int)(mfy * sin(-alpha + mfx)); pa.setPoint(1, x, y); pa.setPoint(2, mx, my); p.drawPolygon(pa); } mAlpha -= mfx; return mwait; } //---------------------------------------------------------------------------- int kSlideShowSaver::effectSpiralIn(bool aInit) { if (aInit) { mw = width(); mh = height(); mix = mw / 8; miy = mh / 8; mx0 = 0; mx1 = mw - mix; my0 = miy; my1 = mh - miy; mdx = mix; mdy = 0; mi = 0; mj = 16 * 16; mx = 0; my = 0; } if (mi==0 && mx0>=mx1) { showNextScreen(); return -1; } if (mi==0 && mx>=mx1) // switch to: down on right side { mi = 1; mdx = 0; mdy = miy; mx1 -= mix; } else if (mi==1 && my>=my1) // switch to: right to left on bottom side { mi = 2; mdx = -mix; mdy = 0; my1 -= miy; } else if (mi==2 && mx<=mx0) // switch to: up on left side { mi = 3; mdx = 0; mdy = -miy; mx0 += mix; } else if (mi==3 && my<=my0) // switch to: left to right on top side { mi = 0; mdx = mix; mdy = 0; my0 += miy; } QPainter p(this); p.drawPixmap(mx, my, mNextScreen, mx, my, mix, miy); mx += mdx; my += mdy; mj--; return 8; } //---------------------------------------------------------------------------- int kSlideShowSaver::effectMeltdown(bool aInit) { int i, x, y; bool done; if (aInit) { delete [] mIntArray; mw = width(); mh = height(); mdx = 4; mdy = 16; mix = mw / mdx; mIntArray = new int[mix]; for (i=mix-1; i>=0; i--) mIntArray[i] = 0; } done = true; QPainter p(this); for (i=0,x=0; i= mh) continue; done = false; if ((KRandom::random()&15) < 6) continue; p.drawPixmap(x, y, mNextScreen, x, y, mdx, mdy); mIntArray[i] += mdy; } if (done) { delete [] mIntArray; mIntArray = NULL; return -1; } return 15; } //---------------------------------------------------------------------------- int kSlideShowSaver::effectCircleOut(bool aInit) { int x, y; static QPolygon pa(4); if (aInit) { mw = width(); mh = height(); mx = mw; my = mh>>1; mAlpha = 2*M_PI; pa.setPoint(0, mw>>1, mh>>1); pa.setPoint(3, mw>>1, mh>>1); mfx = M_PI/16; // divisor must be powers of 8 mfy = sqrt((double)mw*mw + mh*mh) / 2; } if (mAlpha < 0) { showNextScreen(); return -1; } x = mx; y = my; mx = (mw>>1) + (int)(mfy * cos(mAlpha)); my = (mh>>1) + (int)(mfy * sin(mAlpha)); mAlpha -= mfx; pa.setPoint(1, x, y); pa.setPoint(2, mx, my); QBrush brush; brush.setTexture(mNextScreen); QPainter p(this); p.setBrush(brush); p.setPen(Qt::NoPen); p.drawPolygon(pa); return 20; } //---------------------------------------------------------------------------- int kSlideShowSaver::effectSweep(bool aInit) { int w, h, x, y, i; if (aInit) { // subtype: 0=sweep right to left, 1=sweep left to right // 2=sweep bottom to top, 3=sweep top to bottom mSubType = KRandom::random() % 4; mw = width(); mh = height(); mdx = (mSubType==1 ? 16 : -16); mdy = (mSubType==3 ? 16 : -16); mx = (mSubType==1 ? 0 : mw); my = (mSubType==3 ? 0 : mh); } QPainter p(this); if (mSubType==0 || mSubType==1) { // horizontal sweep if ((mSubType==0 && mx < -64) || (mSubType==1 && mx > mw+64)) { return -1; } for (w=2,i=4,x=mx; i>0; i--, w<<=1, x-=mdx) { p.drawPixmap(x, 0, mNextScreen, x, 0, w, mh); } mx += mdx; } else { // vertical sweep if ((mSubType==2 && my < -64) || (mSubType==3 && my > mh+64)) { return -1; } for (h=2,i=4,y=my; i>0; i--, h<<=1, y-=mdy) { p.drawPixmap(0, y, mNextScreen, 0, y, mw, h); } my += mdy; } return 20; } //---------------------------------------------------------------------------- int kSlideShowSaver::effectBlobs(bool aInit) { int r; if (aInit) { mAlpha = M_PI * 2; mw = width(); mh = height(); mi = 150; } if (mi <= 0) { showNextScreen(); return -1; } r = (KRandom::random() % 100) + 100; mx = KRandom::random() % (mw + r); my = KRandom::random() % (mh + r); QBrush brush; brush.setTexture(mNextScreen); QPainter p(this); p.setBrush(brush); p.setPen(Qt::NoPen); p.drawEllipse(mx-r, my-r, r, r); mi--; return 10; } //---------------------------------------------------------------------------- int kSlideShowSaver::effectRandom(bool /*aInit*/) { int x, y, i, w, h, fact, sz; fact = (KRandom::random() % 3) + 1; w = width() >> fact; h = height() >> fact; sz = 1 << fact; QPainter p(this); for (i = (w*h)<<1; i > 0; i--) { x = (KRandom::random() % w) << fact; y = (KRandom::random() % h) << fact; p.drawPixmap(x, y, mNextScreen, x, y, sz, sz); } p.end(); showNextScreen(); return -1; } //---------------------------------------------------------------------------- int kSlideShowSaver::effectGrowing(bool aInit) { if (aInit) { mw = width(); mh = height(); mx = mw >> 1; my = mh >> 1; mi = 0; mfx = mx / 100.0; mfy = my / 100.0; } mx = (mw>>1) - (int)(mi * mfx); my = (mh>>1) - (int)(mi * mfy); mi++; if (mx<0 || my<0) { showNextScreen(); return -1; } if((mw - (mx<<1) == 0) && (mh - (my<<1) == 0)) return 1; QPainter p(this); p.drawPixmap(mx, my, mNextScreen, mx, my, mw - (mx<<1), mh - (my<<1)); return 20; } //---------------------------------------------------------------------------- int kSlideShowSaver::effectChessboard(bool aInit) { int y; if (aInit) { mw = width(); mh = height(); mdx = 8; // width of one tile mdy = 8; // height of one tile mj = (mw+mdx-1)/mdx; // number of tiles mx = mj*mdx; // shrinking x-offset from screen border mix = 0; // growing x-offset from screen border miy = 0; // 0 or mdy for growing tiling effect my = mj&1 ? 0 : mdy; // 0 or mdy for shrinking tiling effect mwait = 800 / mj; // timeout between effects } if (mix >= mw) { showNextScreen(); return -1; } mix += mdx; mx -= mdx; miy = miy ? 0 : mdy; my = my ? 0 : mdy; QPainter p(this); for (y=0; y> 1; miy = mh >> 1; mfx = mix / 100.0; mfy = miy / 100.0; mi = 0; mSubType = KRandom::random() & 1; } mx = (int)(mfx * mi); my = (int)(mfy * mi); if (mx>mix || my>miy) { showNextScreen(); return -1; } x1 = mw - mx; y1 = mh - my; mi++; if((mx==0) && (my==0)) return 1; // otherwise drawPixmap draws the bottom-right of mNextScreen QPainter p(this); if (mSubType) { // moving image edges p.drawPixmap(0, 0, mNextScreen, mix-mx, miy-my, mx, my); p.drawPixmap(x1, 0, mNextScreen, mix, miy-my, mx, my); p.drawPixmap(0, y1, mNextScreen, mix-mx, miy, mx, my); p.drawPixmap(x1, y1, mNextScreen, mix, miy, mx, my); } else { // fixed image edges p.drawPixmap(0, 0, mNextScreen, 0, 0, mx, my); p.drawPixmap(x1, 0, mNextScreen, x1, 0, mx, my); p.drawPixmap(0, y1, mNextScreen, 0, y1, mx, my); p.drawPixmap(x1, y1, mNextScreen, x1, y1, mx, my); } return 20; } //---------------------------------------------------------------------------- int kSlideShowSaver::effectHorizLines(bool aInit) { static int iyPos[] = { 0, 4, 2, 6, 1, 5, 3, 7, -1 }; int y; if (aInit) { mw = width(); mh = height(); mi = 0; } if (iyPos[mi] < 0) return -1; QPainter p(this); for (y=iyPos[mi]; y= 0) return 160; return -1; } //---------------------------------------------------------------------------- int kSlideShowSaver::effectVertLines(bool aInit) { static int ixPos[] = { 0, 4, 2, 6, 1, 5, 3, 7, -1 }; int x; if (aInit) { mw = width(); mh = height(); mi = 0; } if (ixPos[mi] < 0) return -1; QPainter p(this); for (x=ixPos[mi]; x= 0) return 160; return -1; } //----------------------------------------------------------------------------- void kSlideShowSaver::restart() { mEffectRunning = false; mEffect = NULL; update(); } //----------------------------------------------------------------------------- void kSlideShowSaver::paintEvent(QPaintEvent *) { int tmout = -1; int i; if (mEffectRunning) { tmout = (this->*mEffect)(false); } else { loadNextImage(); createNextScreen(); if (mNumEffects == 0) { showNextScreen(); tmout = -1; } else { if (mNumEffects > 1) i = KRandom::random() % mNumEffects; else i = 0; mEffect = mEffectList[i]; mEffectRunning = true; tmout = (this->*mEffect)(true); } } if (tmout <= 0) { tmout = mDelay; mEffectRunning = false; } mTimer.start(tmout); } //---------------------------------------------------------------------------- void kSlideShowSaver::showNextScreen() { QPainter p(this); p.drawPixmap(0, 0, mNextScreen, 0, 0, mNextScreen.width(), mNextScreen.height()); } //---------------------------------------------------------------------------- void kSlideShowSaver::createNextScreen() { QPainter p; int ww, wh, iw, ih, x, y; double fx, fy; if (mNextScreen.size() != size()) mNextScreen = QPixmap(size()); mNextScreen.fill(Qt::black); p.begin(&mNextScreen); foreach( const mScreenGeo& geo, mGeoList ) { loadNextImage(); iw = mImage.width(); ih = mImage.height(); ww = geo.mW; wh = geo.mH; if (mFileList.isEmpty()) { p.setPen(QColor("white")); p.drawText(20 + (KRandom::random() % (ww>>1)), 20 + (KRandom::random() % (wh>>1)), i18n("No images found")); } else { if (mZoomImages) { fx = (double)ww / iw; fy = (double)wh / ih; if (fx > fy) fx = fy; if (fx > 2) fx = 2; iw = (int)(iw * fx); ih = (int)(ih * fx); QImage scaledImg = mImage.scaled(iw, ih, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); x = ((ww - iw) >> 1) + geo.mXorg; y = ((wh - ih) >> 1) + geo.mYorg; p.drawImage(x, y, scaledImg); } else { if(iw >= ww || ih >= wh) { fx = (double)ww / iw; fy = (double)wh / ih; if (fx > fy) fx = fy; if (fx > 2) fx = 2; iw = (int)(iw * fx); ih = (int)(ih * fx); QImage scaledImg = mImage.scaled(iw, ih, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); x = ((ww - iw) >> 1) + geo.mXorg; y = ((wh - ih) >> 1) + geo.mYorg; p.drawImage(x, y, scaledImg); } else { if(mRandomPosition) { x = (KRandom::random() % (ww - iw)) + geo.mXorg; y = (KRandom::random() % (wh - ih)) + geo.mYorg; } else { x = ((ww - iw) >> 1) + geo.mXorg; y = ((wh - ih) >> 1) + geo.mYorg; } // bitBlt(&mNextScreen, x, y, &mImage, 0, 0, iw, ih); p.drawImage(x, y, mImage); } } if (mPrintName) { p.setPen(QColor("black")); for (x=9; x<=11; x++) for (y=21; y>=19; y--) p.drawText(x + geo.mXorg, wh-y+geo.mYorg, mImageName); p.setPen(QColor("white")); p.drawText(10 + geo.mXorg, wh-20 + geo.mYorg, mImageName); } } } p.end(); } //---------------------------------------------------------------------------- void kSlideShowSaver::loadNextImage() { QString fname; int num; nexttry: num = mFileList.count(); if (num <= 0) //no files in the directory { return; } if (mShowRandom) { mFileIdx = KRandom::random() % num; fname = mFileList.takeAt(mFileIdx); if (num == 1) //we're about to run out of images { mFileList = mRandomList; } } else { if (mFileIdx >= num) mFileIdx = 0; fname = mFileList[mFileIdx]; } if (!mImage.load(fname)) { kDebug() << "Failed to load image " << fname; mFileList.removeAll(fname); mRandomList.removeAll(fname); goto nexttry; } mFileIdx++; #ifdef HAVE_KEXIV2 KExiv2Iface::KExiv2 exiv(fname); exiv.rotateExifQImage(mImage, exiv.getImageOrientation()); #endif mImageName = mPrintPath ? fname : QFileInfo(fname).completeBaseName(); } //---------------------------------------------------------------------------- void kSlideShowSaver::loadDirectory() { mFileIdx = 0; mFileList.clear(); traverseDirectory(mDirectory); mRandomList = mFileList; } void kSlideShowSaver::traverseDirectory(const QString &dirName) { QDir dir(dirName); if (!dir.exists()) { return ; } dir.setFilter(QDir::Dirs | QDir::Files); QFileInfoList fileinfolist = dir.entryInfoList(); QFileInfoList::Iterator it = fileinfolist.begin(); while ( it != fileinfolist.end()) { if (it->fileName() == QLatin1String( "." ) || it->fileName() == QLatin1String( ".." )) { ++it; continue; } if (it->isDir() && it->isReadable() && mSubdirectory) { traverseDirectory(it->filePath()); } else { if (!it->isDir()) { mFileList.append(it->filePath()); } } ++it; } } //============================================================================= // Class kSlideShowSetup //============================================================================= kSlideShowSetup::kSlideShowSetup(QWidget *aParent) : KDialog(aParent) { setCaption(i18n( "Setup Slide Show Screen Saver" )); setButtons(Ok|Cancel|Help); setDefaultButton(Ok); setModal(true); setButtonText( Help, i18n( "A&bout" ) ); QWidget *main = new QWidget(this); setMainWidget(main); cfg = new SlideShowCfg(main); cfg->mPreview->setFixedSize(220, 170); cfg->mPreview->show(); // otherwise saver does not get correct size mSaver = new kSlideShowSaver(cfg->mPreview->winId()); cfg->mDirChooser->setMode(KFile::Directory | KFile::ExistingOnly); connect(cfg->mDirChooser, SIGNAL(returnPressed(QString)), SLOT(slotDirSelected(QString))); connect(cfg->mDirChooser, SIGNAL(urlSelected(KUrl)), SLOT(slotDirSelected(KUrl))); connect(this,SIGNAL(okClicked()),this,SLOT(slotOk())); connect(this,SIGNAL(helpClicked()),this,SLOT(slotHelp())); readSettings(); } kSlideShowSetup::~kSlideShowSetup() { delete mSaver; } //----------------------------------------------------------------------------- void kSlideShowSetup::readSettings() { KConfigGroup config( KGlobal::config(), "Settings"); cfg->mCbxRandom->setChecked(config.readEntry("ShowRandom", true)); cfg->mCbxZoom->setChecked(config.readEntry("ZoomImages", false)); cfg->mCbxShowName->setChecked(config.readEntry("PrintName", true)); cfg->mCbxShowPath->setChecked(config.readEntry("PrintPath", false)); cfg->mDelay->setValue(config.readEntry("Delay", 20)); cfg->mDelay->setSuffix(ki18np(" second", " seconds")); cfg->mDirChooser->setUrl(KUrl(config.readPathEntry("Directory", QString()))); cfg->mCbxSubdirectory->setChecked(config.readEntry("SubDirectory", false)); cfg->mCbxRandomPosition->setChecked(config.readEntry("RandomPosition", false)); //cfg->mCbxEffectsEnabled->setChecked(config.readEntry("EffectsEnabled", true)); } //----------------------------------------------------------------------------- void kSlideShowSetup::writeSettings() { KConfigGroup config( KGlobal::config(), "Settings"); config.writeEntry("ShowRandom", cfg->mCbxRandom->isChecked()); config.writeEntry("ZoomImages", cfg->mCbxZoom->isChecked()); config.writeEntry("PrintName", cfg->mCbxShowName->isChecked()); config.writeEntry("PrintPath", cfg->mCbxShowPath->isChecked()); config.writeEntry("Delay", cfg->mDelay->value()); config.writePathEntry("Directory", cfg->mDirChooser->url().path()); config.writeEntry("SubDirectory", cfg->mCbxSubdirectory->isChecked()); config.writeEntry("RandomPosition", cfg->mCbxRandomPosition->isChecked()); //config.writeEntry("EffectsEnabled", cfg->mCbxEffectsEnabled->isChecked()); config.sync(); if (mSaver) { mSaver->readConfig(); mSaver->restart(); } } //----------------------------------------------------------------------------- void kSlideShowSetup::slotDirSelected(const KUrl &) { writeSettings(); } void kSlideShowSetup::slotDirSelected(const QString &) { writeSettings(); } //----------------------------------------------------------------------------- void kSlideShowSetup::slotOk() { writeSettings(); accept(); } //----------------------------------------------------------------------------- void kSlideShowSetup::slotHelp() { KAboutApplicationDialog mAbout(s_aboutData, this); mAbout.exec(); }