delay a few things on startup, such as setting the visibility mode, which ensures...
[personal-kdebase.git] / workspace / plasma / wallpapers / image / image.cpp
blob78d4971f1754f1c5e0c6e8b1968f211b6ec1f550
1 /*
2 Copyright (c) 2007 by Paolo Capriotti <p.capriotti@gmail.com>
3 Copyright (c) 2007 by Aaron Seigo <aseigo@kde.org>
4 Copyright (c) 2008 by Alexis Ménard <darktears31@gmail.com>
5 Copyright (c) 2008 by Petri Damsten <damu@iki.fi>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
13 #include "image.h"
15 #include <QPainter>
16 #include <QFile>
18 #include <KDirSelectDialog>
19 #include <KDirWatch>
20 #include <KFileDialog>
21 #include <KGlobalSettings>
22 #include <KImageFilePreview>
23 #include <KNS/Engine>
24 #include <KRandom>
25 #include <KStandardDirs>
27 #include <Plasma/Theme>
28 #include "backgroundlistmodel.h"
29 #include "backgrounddelegate.h"
30 #include "ksmserver_interface.h"
33 Image::Image(QObject *parent, const QVariantList &args)
34 : Plasma::Wallpaper(parent, args),
35 m_currentSlide(-1),
36 m_model(0),
37 m_dialog(0),
38 m_rendererToken(-1),
39 m_randomize(true)
41 qRegisterMetaType<QImage>("QImage");
42 connect(&m_renderer, SIGNAL(done(int, QImage)), this, SLOT(updateBackground(int, QImage)));
43 connect(&m_timer, SIGNAL(timeout()), this, SLOT(nextSlide()));
46 Image::~Image()
48 qDeleteAll(m_slideshowBackgrounds);
51 void Image::init(const KConfigGroup &config)
53 m_timer.stop();
54 m_mode = renderingMode().name();
55 calculateGeometry();
57 m_delay = config.readEntry("slideTimer", 600);
58 m_resizeMethod = (Background::ResizeMethod)config.readEntry("wallpaperposition",
59 (int)Background::Scale);
60 m_wallpaper = config.readEntry("wallpaper", QString());
61 if (m_wallpaper.isEmpty()) {
62 m_wallpaper = Plasma::Theme::defaultTheme()->wallpaperPath();
63 int index = m_wallpaper.indexOf("/contents/images/");
64 if (index > -1) { // We have file from package -> get path to package
65 m_wallpaper = m_wallpaper.left(index);
69 m_color = config.readEntry("wallpapercolor", QColor(56, 111, 150));
70 m_usersWallpapers = config.readEntry("userswallpapers", QStringList());
71 m_dirs = config.readEntry("slidepaths", QStringList());
73 if (m_dirs.isEmpty()) {
74 m_dirs << KStandardDirs::installPath("wallpaper");
77 if (m_mode == "SingleImage") {
78 setSingleImage();
79 } else {
80 startSlideshow();
84 void Image::save(KConfigGroup &config)
86 config.writeEntry("slideTimer", m_delay);
87 config.writeEntry("wallpaperposition", (int)m_resizeMethod);
88 config.writeEntry("slidepaths", m_dirs);
89 config.writeEntry("wallpaper", m_wallpaper);
90 config.writeEntry("wallpapercolor", m_color);
91 config.writeEntry("userswallpapers", m_usersWallpapers);
94 QWidget* Image::createConfigurationInterface(QWidget* parent)
96 m_widget = new QWidget(parent);
98 if (m_mode == "SingleImage") {
99 m_uiImage.setupUi(m_widget);
101 m_model = new BackgroundListModel(m_ratio, this);
102 m_uiImage.m_view->setModel(m_model);
103 m_uiImage.m_view->setItemDelegate(new BackgroundDelegate(m_uiImage.m_view->view(),
104 m_ratio, this));
105 m_uiImage.m_view->view()->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
106 m_model->reload(m_usersWallpapers);
107 int index = m_model->indexOf(m_wallpaper);
108 if (index != -1) {
109 m_uiImage.m_view->setCurrentIndex(index);
110 Background *b = m_model->package(index);
111 if (b) {
112 fillMetaInfo(b);
115 connect(m_uiImage.m_view, SIGNAL(currentIndexChanged(int)), this, SLOT(pictureChanged(int)));
117 m_uiImage.m_pictureUrlButton->setIcon(KIcon("document-open"));
118 connect(m_uiImage.m_pictureUrlButton, SIGNAL(clicked()), this, SLOT(showFileDialog()));
120 m_uiImage.m_emailLine->setTextInteractionFlags(Qt::TextSelectableByMouse);
122 m_uiImage.m_resizeMethod->addItem(i18n("Scaled & Cropped"), Background::ScaleCrop);
123 m_uiImage.m_resizeMethod->addItem(i18n("Scaled"), Background::Scale);
124 m_uiImage.m_resizeMethod->addItem(i18n("Scaled, keep proportions"), Background::Maxpect);
125 m_uiImage.m_resizeMethod->addItem(i18n("Centered"), Background::Center);
126 m_uiImage.m_resizeMethod->addItem(i18n("Tiled"), Background::Tiled);
127 m_uiImage.m_resizeMethod->addItem(i18n("Center Tiled"), Background::CenterTiled);
128 for (int i = 0; i < m_uiImage.m_resizeMethod->count(); ++i) {
129 if (m_resizeMethod == m_uiImage.m_resizeMethod->itemData(i).value<int>()) {
130 m_uiImage.m_resizeMethod->setCurrentIndex(i);
131 break;
134 connect(m_uiImage.m_resizeMethod, SIGNAL(currentIndexChanged(int)),
135 this, SLOT(positioningChanged(int)));
137 m_uiImage.m_color->setColor(m_color);
138 connect(m_uiImage.m_color, SIGNAL(changed(const QColor&)), this, SLOT(colorChanged(const QColor&)));
140 connect(m_uiImage.m_newStuff, SIGNAL(clicked()), this, SLOT(getNewWallpaper()));
141 } else {
142 m_uiSlideshow.setupUi(m_widget);
144 m_uiSlideshow.m_dirlist->clear();
145 foreach (const QString &dir, m_dirs) {
146 m_uiSlideshow.m_dirlist->addItem(dir);
148 m_uiSlideshow.m_dirlist->setCurrentRow(0);
149 updateDirs();
150 m_uiSlideshow.m_addDir->setIcon(KIcon("list-add"));
151 connect(m_uiSlideshow.m_addDir, SIGNAL(clicked()), this, SLOT(slotAddDir()));
152 m_uiSlideshow.m_removeDir->setIcon(KIcon("list-remove"));
153 connect(m_uiSlideshow.m_removeDir, SIGNAL(clicked()), this, SLOT(slotRemoveDir()));
155 QTime time(0, 0, 0);
156 time = time.addSecs(m_delay);
157 m_uiSlideshow.m_slideshowDelay->setTime(time);
158 m_uiSlideshow.m_slideshowDelay->setMinimumTime(QTime(0, 0, 30));
159 connect(m_uiSlideshow.m_slideshowDelay, SIGNAL(timeChanged(const QTime&)),
160 this, SLOT(timeChanged(const QTime&)));
162 m_uiSlideshow.m_resizeMethod->addItem(i18n("Scaled & Cropped"), Background::ScaleCrop);
163 m_uiSlideshow.m_resizeMethod->addItem(i18n("Scaled"), Background::Scale);
164 m_uiSlideshow.m_resizeMethod->addItem(i18n("Scaled, keep proportions"), Background::Maxpect);
165 m_uiSlideshow.m_resizeMethod->addItem(i18n("Centered"), Background::Center);
166 m_uiSlideshow.m_resizeMethod->addItem(i18n("Tiled"), Background::Tiled);
167 m_uiSlideshow.m_resizeMethod->addItem(i18n("Center Tiled"), Background::CenterTiled);
168 for (int i = 0; i < m_uiSlideshow.m_resizeMethod->count(); ++i) {
169 if (m_resizeMethod == m_uiSlideshow.m_resizeMethod->itemData(i).value<int>()) {
170 m_uiSlideshow.m_resizeMethod->setCurrentIndex(i);
171 break;
174 connect(m_uiSlideshow.m_resizeMethod, SIGNAL(currentIndexChanged(int)),
175 this, SLOT(positioningChanged(int)));
177 m_uiSlideshow.m_color->setColor(m_color);
178 connect(m_uiSlideshow.m_color, SIGNAL(changed(const QColor&)), this, SLOT(colorChanged(const QColor&)));
181 return m_widget;
184 void Image::calculateGeometry()
186 m_size = boundingRect().size().toSize();
187 m_renderer.setSize(m_size);
188 m_ratio = boundingRect().width() / boundingRect().height();
189 m_renderer.setRatio(m_ratio);
192 void Image::paint(QPainter *painter, const QRectF& exposedRect)
194 // Check if geometry changed
195 //kDebug() << m_size << boundingRect().size().toSize();
196 if (m_size != boundingRect().size().toSize()) {
197 calculateGeometry();
198 if (!m_img.isEmpty()) { // We have previous image
199 render();
200 //kDebug() << "re-rendering";
201 return;
205 if (m_pixmap.isNull()) {
206 painter->fillRect(exposedRect, QBrush(m_color));
207 //kDebug() << "pixmap null";
208 return;
211 painter->save();
213 if (painter->worldMatrix() == QMatrix()) {
214 // draw the background untransformed when possible;(saves lots of per-pixel-math)
215 painter->resetTransform();
218 // blit the background (saves all the per-pixel-products that blending does)
219 painter->setCompositionMode(QPainter::CompositionMode_Source);
221 // for pixmaps we draw only the exposed part (untransformed since the
222 // bitmapBackground already has the size of the viewport)
223 painter->drawPixmap(exposedRect, m_pixmap, exposedRect);
225 // restore transformation and composition mode
226 painter->restore();
229 void Image::timeChanged(const QTime& time)
231 m_delay = QTime(0, 0, 0).secsTo(time);
232 if (!m_slideshowBackgrounds.isEmpty()) {
233 m_timer.start(m_delay * 1000);
237 void Image::slotAddDir()
239 KUrl empty;
240 KDirSelectDialog dialog(empty, true, m_widget);
241 if (dialog.exec()) {
242 QString urlDir = dialog.url().path();
243 if (!urlDir.isEmpty() && m_uiSlideshow.m_dirlist->findItems(urlDir, Qt::MatchExactly).isEmpty()) {
244 m_uiSlideshow.m_dirlist->addItem(dialog.url().path());
245 updateDirs();
246 startSlideshow();
251 void Image::slotRemoveDir()
253 int row = m_uiSlideshow.m_dirlist->currentRow();
254 if (row != -1) {
255 m_uiSlideshow.m_dirlist->takeItem(row);
256 updateDirs();
257 startSlideshow();
261 void Image::updateDirs()
263 m_dirs.clear();
264 for (int i = 0; i < m_uiSlideshow.m_dirlist->count(); i++) {
265 m_dirs.append(m_uiSlideshow.m_dirlist->item(i)->text());
268 if (m_uiSlideshow.m_dirlist->count() == 0) {
269 m_uiSlideshow.m_dirlist->hide();
270 } else {
271 const int itemHeight = m_uiSlideshow.m_dirlist->visualItemRect(m_uiSlideshow.m_dirlist->item(0)).height();
272 const int vMargin = m_uiSlideshow.m_dirlist->height() - m_uiSlideshow.m_dirlist->viewport()->height();
274 if (m_uiSlideshow.m_dirlist->count() <= 6) {
275 m_uiSlideshow.m_dirlist->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
276 m_uiSlideshow.m_dirlist->setFixedHeight(itemHeight * m_uiSlideshow.m_dirlist->count() + vMargin);
277 } else {
278 m_uiSlideshow.m_dirlist->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
281 if (!m_uiSlideshow.m_dirlist->isVisible()) {
282 m_uiSlideshow.m_dirlist->setCurrentRow(0);
285 m_uiSlideshow.m_dirlist->show();
286 m_uiSlideshow.gridLayout->invalidate();
289 m_uiSlideshow.m_removeDir->setEnabled(m_uiSlideshow.m_dirlist->currentRow() != -1);
292 void Image::setSingleImage()
294 QString img;
295 BackgroundPackage b(m_wallpaper, m_ratio);
297 img = b.findBackground(m_size, m_resizeMethod); // isValid() returns true for jpg?
298 kDebug() << img << m_wallpaper;
299 if (img.isEmpty()) {
300 img = m_wallpaper;
303 render(img);
306 void Image::startSlideshow()
308 // populate background list
309 m_timer.stop();
310 qDeleteAll(m_slideshowBackgrounds);
311 m_slideshowBackgrounds.clear();
312 foreach (const QString& dir, m_dirs) {
313 m_slideshowBackgrounds += BackgroundListModel::findAllBackgrounds(0, dir, m_ratio);
316 // start slideshow
317 if (m_slideshowBackgrounds.isEmpty()) {
318 m_pixmap = QPixmap();
319 emit update(boundingRect());
320 } else {
321 m_currentSlide = -1;
322 nextSlide();
323 m_timer.start(m_delay * 1000);
327 void Image::getNewWallpaper()
329 KNS::Engine engine(m_widget);
330 if (engine.init("wallpaper.knsrc")) {
331 KNS::Entry::List entries = engine.downloadDialogModal(m_widget);
333 if (entries.size() > 0 && m_model) {
334 m_model->reload();
339 void Image::colorChanged(const QColor& color)
341 m_color = color;
342 setSingleImage();
345 void Image::pictureChanged(int index)
347 if (index == -1 || !m_model) {
348 return;
351 Background *b = m_model->package(index);
352 if (!b) {
353 return;
356 fillMetaInfo(b);
357 m_wallpaper = b->path();
358 setSingleImage();
361 void Image::positioningChanged(int index)
363 if (m_mode == "SingleImage") {
364 m_resizeMethod =
365 (Background::ResizeMethod)m_uiImage.m_resizeMethod->itemData(index).value<int>();
366 setSingleImage();
367 } else {
368 m_resizeMethod =
369 (Background::ResizeMethod)m_uiSlideshow.m_resizeMethod->itemData(index).value<int>();
370 startSlideshow();
374 void Image::fillMetaInfo(Background *b)
376 // Prepare more user-friendly forms of some pieces of data.
377 // - license by config is more a of a key value,
378 // try to get the proper name if one of known licenses.
380 // not needed for now...
381 //QString license = b->license();
383 KAboutLicense knownLicense = KAboutLicense::byKeyword(license);
384 if (knownLicense.key() != KAboutData::License_Custom) {
385 license = knownLicense.name(KAboutData::ShortName);
388 // - last ditch attempt to localize author's name, if not such by config
389 // (translators can "hook" names from outside if resolute enough).
390 if (!b->author().isEmpty()) {
391 QString author = i18nc("Wallpaper info, author name", "%1", b->author());
392 m_uiImage.m_authorLabel->setAlignment(Qt::AlignRight);
393 setMetadata(m_uiImage.m_authorLine, author);
394 } else {
395 setMetadata(m_uiImage.m_authorLine, QString());
396 m_uiImage.m_authorLabel->setAlignment(Qt::AlignLeft);
398 setMetadata(m_uiImage.m_licenseLine, QString());
399 setMetadata(m_uiImage.m_emailLine, QString());
400 m_uiImage.m_emailLabel->hide();
401 m_uiImage.m_licenseLabel->hide();
404 bool Image::setMetadata(QLabel *label, const QString &text)
406 if (text.isEmpty()) {
407 label->hide();
408 return false;
410 else {
411 label->show();
412 label->setText(text);
413 return true;
417 void Image::showFileDialog()
419 if (!m_dialog) {
420 m_dialog = new KFileDialog(KUrl(), "*.png *.jpeg *.jpg *.svg *.svgz", m_widget);
421 m_dialog->setOperationMode(KFileDialog::Opening);
422 m_dialog->setInlinePreviewShown(true);
423 m_dialog->setCaption(i18n("Select Wallpaper Image File"));
424 m_dialog->setModal(false);
426 m_dialog->show();
427 m_dialog->raise();
428 m_dialog->activateWindow();
430 connect(m_dialog, SIGNAL(okClicked()), this, SLOT(browse()));
433 void Image::browse()
435 Q_ASSERT(m_model);
437 QString wallpaper = m_dialog->selectedFile();
438 disconnect(m_dialog, SIGNAL(okClicked()), this, SLOT(browse()));
440 if (wallpaper.isEmpty()) {
441 return;
444 if (m_model->contains(wallpaper)) {
445 m_uiImage.m_view->setCurrentIndex(m_model->indexOf(wallpaper));
446 return;
449 // add background to the model
450 m_model->addBackground(wallpaper);
452 // select it
453 int index = m_model->indexOf(wallpaper);
454 if (index != -1) {
455 m_uiImage.m_view->setCurrentIndex(index);
457 // save it
458 m_usersWallpapers << wallpaper;
461 void Image::nextSlide()
463 if (m_slideshowBackgrounds.size() < 1) {
464 return;
467 QString previous;
468 if (m_currentSlide >= 0 && m_currentSlide < m_slideshowBackgrounds.size()) {
469 previous = m_slideshowBackgrounds[m_currentSlide]->findBackground(m_size, m_resizeMethod);
472 if (m_randomize) {
473 m_currentSlide = KRandom::random() % m_slideshowBackgrounds.size();
474 } else if (++m_currentSlide >= m_slideshowBackgrounds.size()) {
475 m_currentSlide = 0;
478 QString current = m_slideshowBackgrounds[m_currentSlide]->findBackground(m_size, m_resizeMethod);
479 if (current == previous) {
480 QFileInfo info(previous);
481 if (m_previousModified == info.lastModified()) {
482 // it hasn't changed since we last loaded it, so try the next one instead
483 if (m_slideshowBackgrounds.count() == 1) {
484 // only one slide, same image, continue on
485 return;
488 if (++m_currentSlide >= m_slideshowBackgrounds.size()) {
489 m_currentSlide = 0;
492 current = m_slideshowBackgrounds[m_currentSlide]->findBackground(m_size, m_resizeMethod);
496 QFileInfo info(current);
497 m_previousModified = info.lastModified();
499 render(current);
502 void Image::render(const QString& image)
504 if (!image.isEmpty()) {
505 m_img = image;
507 // else re-render previous image
508 m_rendererToken = m_renderer.render(m_img, m_color, m_resizeMethod,
509 Qt::SmoothTransformation);
510 suspendStartup(true); // during KDE startup, make ksmserver until the wallpaper is ready
513 void Image::updateBackground(int token, const QImage &img)
515 if (m_rendererToken == token) {
516 m_pixmap = QPixmap::fromImage(img);
517 emit update(boundingRect());
518 suspendStartup(false);
522 void Image::suspendStartup(bool suspend)
524 org::kde::KSMServerInterface ksmserver("org.kde.ksmserver", "/KSMServer", QDBusConnection::sessionBus());
525 const QString startupID("desktop wallaper");
526 if (suspend) {
527 ksmserver.suspendStartup(startupID);
528 } else {
529 ksmserver.resumeStartup(startupID);
533 void Image::updateScreenshot(QPersistentModelIndex index)
535 m_uiImage.m_view->view()->update(index);
538 void Image::removeBackground(const QString &path)
540 if (m_model) {
541 m_model->removeBackground(path);
545 #include "image.moc"