Ran am2cmake.
[basket4.git] / src / backgroundmanager.cpp
blob84da55c78d5f8dc5e61d1a9a9c9c9658736ec8c1
1 /***************************************************************************
2 * Copyright (C) 2003 by S�astien Laot *
3 * slaout@linux62.org *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include <kurl.h>
22 #include <kglobal.h>
23 #include <kstandarddirs.h>
24 #include <ksimpleconfig.h>
25 #include <qpainter.h>
26 #include <qdir.h>
27 #include <qimage.h>
28 //Added by qt3to4:
29 #include <QPixmap>
31 #include <iostream>
33 #include "backgroundmanager.h"
35 /** class BackgroundEntry: */
37 BackgroundEntry::BackgroundEntry(const QString &location)
39 this->location = location;
40 name = KURL(location).fileName();
41 tiled = false;
42 pixmap = 0;
43 preview = 0;
44 customersCount = 0;
47 BackgroundEntry::~BackgroundEntry()
49 delete pixmap;
50 delete preview;
53 /** class OpaqueBackgroundEntry: */
55 OpaqueBackgroundEntry::OpaqueBackgroundEntry(const QString &name, const QColor &color)
57 this->name = name;
58 this->color = color;
59 pixmap = 0;
60 customersCount = 0;
63 OpaqueBackgroundEntry::~OpaqueBackgroundEntry()
65 delete pixmap;
68 /** class BackgroundManager: */
70 BackgroundManager::BackgroundManager()
72 /// std::cout << "BackgroundManager: Found the following background images in ";
73 QStringList directories = KGlobal::dirs()->resourceDirs("data"); // eg. { "/home/seb/.kde/share/apps/", "/usr/share/apps/" }
74 // For each folder:
75 for (QStringList::Iterator it = directories.begin(); it != directories.end(); ++it) {
76 // For each file in those directories:
77 QDir dir(*it + "basket/backgrounds/", /*nameFilder=*/"*.png", /*sortSpec=*/QDir::Name | QDir::IgnoreCase, /*filterSpec=*/QDir::Files | QDir::NoSymLinks);
78 /// std::cout << *it + "basket/backgrounds/ ";
79 QStringList files = dir.entryList();
80 for (QStringList::Iterator it2 = files.begin(); it2 != files.end(); ++it2) // TODO: If an image name is present in two folders?
81 addImage(*it + "basket/backgrounds/" + *it2);
84 /// std::cout << ":" << std::endl;
85 /// for (BackgroundsList::Iterator it = m_backgroundsList.begin(); it != m_backgroundsList.end(); ++it)
86 /// std::cout << "* " << (*it)->location << " [ref: " << (*it)->name << "]" << std::endl;
88 connect( &m_garbageTimer, SIGNAL(timeout()), this, SLOT(doGarbage()) );
91 BackgroundManager::~BackgroundManager()
95 void BackgroundManager::addImage(const QString &fullPath)
97 m_backgroundsList.append(new BackgroundEntry(fullPath));
100 BackgroundEntry* BackgroundManager::backgroundEntryFor(const QString &image)
102 for (BackgroundsList::Iterator it = m_backgroundsList.begin(); it != m_backgroundsList.end(); ++it)
103 if ((*it)->name == image)
104 return *it;
105 return 0;
108 OpaqueBackgroundEntry* BackgroundManager::opaqueBackgroundEntryFor(const QString &image, const QColor &color)
110 for (OpaqueBackgroundsList::Iterator it = m_opaqueBackgroundsList.begin(); it != m_opaqueBackgroundsList.end(); ++it)
111 if ((*it)->name == image && (*it)->color == color)
112 return *it;
113 return 0;
116 bool BackgroundManager::subscribe(const QString &image)
118 BackgroundEntry *entry = backgroundEntryFor(image);
119 if (entry) {
120 // If it's the first time something subscribe to this image:
121 if (!entry->pixmap) {
122 // Try to load the pixmap:
123 entry->pixmap = new QPixmap(entry->location);
124 // Try to figure out if it's a tiled background image or not (default to NO):
125 KSimpleConfig config(entry->location + ".config", /*readOnly=*/true);
126 config.setGroup("BasKet Background Image Configuration");
127 entry->tiled = config.readBoolEntry("tiled", false);
129 // Return if the image loading has failed:
130 if (entry->pixmap->isNull()) {
131 /// std::cout << "BackgroundManager: Failed to load " << entry->location << std::endl;
132 return false;
134 // Success: effectively subscribe:
135 ++entry->customersCount;
136 return true;
137 } else {
138 // Don't exist: subscription failed:
139 /// std::cout << "BackgroundManager: Requested unexisting image: " << image << std::endl;
140 return false;
144 bool BackgroundManager::subscribe(const QString &image, const QColor &color)
146 BackgroundEntry *backgroundEntry = backgroundEntryFor(image);
148 // First, if the image doesn't exist, isn't subscribed, or failed to load then we don't go further:
149 if (!backgroundEntry || !backgroundEntry->pixmap || backgroundEntry->pixmap->isNull()) {
150 /// std::cout << "BackgroundManager: Requested an unexisting or unsubscribed image: (" << image << "," << color.name() << ")..." << std::endl;
151 return false;
154 OpaqueBackgroundEntry *opaqueBackgroundEntry = opaqueBackgroundEntryFor(image, color);
156 // If this couple is requested for the first time or it haven't been subscribed for a long time enough, create it:
157 if (!opaqueBackgroundEntry) {
158 /// std::cout << "BackgroundManager: Computing (" << image << "," << color.name() << ")..." << std::endl;
159 opaqueBackgroundEntry = new OpaqueBackgroundEntry(image, color);
160 opaqueBackgroundEntry->pixmap = new QPixmap(backgroundEntry->pixmap->size());
161 opaqueBackgroundEntry->pixmap->fill(color);
162 QPainter painter(opaqueBackgroundEntry->pixmap);
163 painter.drawPixmap(0, 0, *(backgroundEntry->pixmap));
164 painter.end();
165 m_opaqueBackgroundsList.append(opaqueBackgroundEntry);
168 // We are now sure the entry exist, do the subscription:
169 ++opaqueBackgroundEntry->customersCount;
170 return true;
173 void BackgroundManager::unsubscribe(const QString &image)
175 BackgroundEntry *entry = backgroundEntryFor(image);
177 if (!entry) {
178 /// std::cout << "BackgroundManager: Wanted to unsuscribe a not subscribed image: " << image << std::endl;
179 return;
182 --entry->customersCount;
183 if (entry->customersCount <= 0)
184 requestDelayedGarbage();
187 void BackgroundManager::unsubscribe(const QString &image, const QColor &color)
189 OpaqueBackgroundEntry *entry = opaqueBackgroundEntryFor(image, color);
191 if (!entry) {
192 /// std::cout << "BackgroundManager: Wanted to unsuscribe a not subscribed colored image: (" << image << "," << color.name() << ")" << std::endl;
193 return;
196 --entry->customersCount;
197 if (entry->customersCount <= 0)
198 requestDelayedGarbage();
201 QPixmap* BackgroundManager::pixmap(const QString &image)
203 BackgroundEntry *entry = backgroundEntryFor(image);
205 if (!entry || !entry->pixmap || entry->pixmap->isNull()) {
206 /// std::cout << "BackgroundManager: Requested an unexisting or unsubscribed image: " << image << std::endl;
207 return 0;
210 return entry->pixmap;
213 QPixmap* BackgroundManager::opaquePixmap(const QString &image, const QColor &color)
215 OpaqueBackgroundEntry *entry = opaqueBackgroundEntryFor(image, color);
217 if (!entry || !entry->pixmap || entry->pixmap->isNull()) {
218 /// std::cout << "BackgroundManager: Requested an unexisting or unsubscribed colored image: (" << image << "," << color.name() << ")" << std::endl;
219 return 0;
222 return entry->pixmap;
225 bool BackgroundManager::tiled(const QString &image)
227 BackgroundEntry *entry = backgroundEntryFor(image);
229 if (!entry || !entry->pixmap || entry->pixmap->isNull()) {
230 /// std::cout << "BackgroundManager: Requested an unexisting or unsubscribed image: " << image << std::endl;
231 return false;
234 return entry->tiled;
237 bool BackgroundManager::exists(const QString &image)
239 for (BackgroundsList::iterator it = m_backgroundsList.begin(); it != m_backgroundsList.end(); ++it)
240 if ((*it)->name == image)
241 return true;
242 return false;
245 QStringList BackgroundManager::imageNames()
247 QStringList list;
248 for (BackgroundsList::iterator it = m_backgroundsList.begin(); it != m_backgroundsList.end(); ++it)
249 list.append((*it)->name);
250 return list;
253 QPixmap* BackgroundManager::preview(const QString &image)
255 static const int MAX_WIDTH = 100;
256 static const int MAX_HEIGHT = 75;
257 static const QColor PREVIEW_BG = Qt::white;
259 BackgroundEntry *entry = backgroundEntryFor(image);
261 if (!entry) {
262 /// std::cout << "BackgroundManager: Requested the preview of an unexisting image: " << image << std::endl;
263 return false;
266 // The easiest way: already computed:
267 if (entry->preview)
268 return entry->preview;
270 // Then, try to load the preview from file:
271 QString previewPath = KGlobal::dirs()->findResource("data", "basket/backgrounds/previews/" + entry->name);
272 QPixmap *previewPixmap = new QPixmap(previewPath);
273 // Success:
274 if (!previewPixmap->isNull()) {
275 /// std::cout << "BackgroundManager: Loaded image preview for " << entry->location << " from file " << previewPath << std::endl;
276 entry->preview = previewPixmap;
277 return entry->preview;
280 // We failed? Then construct it:
281 // Note: if a preview is requested, it's because the user is currently choosing an image.
282 // Since we need that image to create the preview, we keep the image in memory.
283 // Then, it will already be loaded when user press [OK] in the background image chooser.
284 // BUT we also delay a garbage because we don't want EVERY images to be loaded if the user use only a few of them, of course:
286 // Already used? Good: we don't have to load it...
287 if (!entry->pixmap) {
288 // Note: it's a code duplication from BackgroundManager::subscribe(const QString &image),
289 // Because, as we are loading the pixmap we ALSO need to know if it's a tile or not, in case that image will soon be used (and not destroyed by the garbager):
290 entry->pixmap = new QPixmap(entry->location);
291 // Try to figure out if it's a tiled background image or not (default to NO):
292 KSimpleConfig config(entry->location + ".config", /*readOnly=*/true);
293 config.setGroup("BasKet Background Image Configuration");
294 entry->tiled = config.readBoolEntry("tiled", false);
297 // The image cannot be loaded, we failed:
298 if (entry->pixmap->isNull())
299 return 0;
301 // Good that we are still alive: entry->pixmap contains the pixmap to rescale down for the preview:
302 // Compute new size:
303 int width = entry->pixmap->width();
304 int height = entry->pixmap->height();
305 if (width > MAX_WIDTH) {
306 height = height * MAX_WIDTH / width;
307 width = MAX_WIDTH;
309 if (height > MAX_HEIGHT) {
310 width = width * MAX_HEIGHT / height;
311 height = MAX_HEIGHT;
313 // And create the resulting pixmap:
314 QPixmap *result = new QPixmap(width, height);
315 result->fill(PREVIEW_BG);
316 QImage imageToScale = entry->pixmap->convertToImage();
317 QPixmap pmScaled;
318 pmScaled.convertFromImage(imageToScale.smoothScale(width, height));
319 QPainter painter(result);
320 painter.drawPixmap(0, 0, pmScaled);
321 painter.end();
323 // Saving it to file for later:
324 QString folder = KGlobal::dirs()->saveLocation("data", "basket/backgrounds/previews/");
325 result->save(folder + entry->name, "PNG");
327 // Ouf! That's done:
328 entry->preview = result;
329 requestDelayedGarbage();
330 return entry->preview;
333 QString BackgroundManager::pathForImageName(const QString &image)
335 BackgroundEntry *entry = backgroundEntryFor(image);
336 if (entry == 0)
337 return "";
338 else
339 return entry->location;
342 QString BackgroundManager::previewPathForImageName(const QString &image)
344 BackgroundEntry *entry = backgroundEntryFor(image);
345 if (entry == 0)
346 return "";
347 else {
348 QString previewPath = KGlobal::dirs()->findResource("data", "basket/backgrounds/previews/" + entry->name);
349 QDir dir;
350 if (!dir.exists(previewPath))
351 return "";
352 else
353 return previewPath;
357 void BackgroundManager::requestDelayedGarbage()
359 static const int DELAY = 60/*seconds*/;
361 if (!m_garbageTimer.isActive())
362 m_garbageTimer.start(DELAY * 1000/*ms*/, /*singleShot=*/true);
365 void BackgroundManager::doGarbage()
367 /// std::cout << "BackgroundManager: Doing garbage..." << std::endl;
369 /// std::cout << "BackgroundManager: Images:" << std::endl;
370 for (BackgroundsList::Iterator it = m_backgroundsList.begin(); it != m_backgroundsList.end(); ++it) {
371 BackgroundEntry *entry = *it;
372 /// std::cout << "* " << entry->name << ": used " << entry->customersCount << " times";
373 if (entry->customersCount <= 0 && entry->pixmap) {
374 /// std::cout << " [Deleted cached pixmap]";
375 delete entry->pixmap;
376 entry->pixmap = 0;
378 /// std::cout << std::endl;
381 /// std::cout << "BackgroundManager: Opaque Cached Images:" << std::endl;
382 for (OpaqueBackgroundsList::Iterator it = m_opaqueBackgroundsList.begin(); it != m_opaqueBackgroundsList.end(); ) {
383 OpaqueBackgroundEntry *entry = *it;
384 /// std::cout << "* " << entry->name << "," << entry->color.name() << ": used " << entry->customersCount << " times";
385 if (entry->customersCount <= 0) {
386 /// std::cout << " [Deleted entry]";
387 delete entry->pixmap;
388 entry->pixmap = 0;
389 it = m_opaqueBackgroundsList.remove(it);
390 } else
391 ++it;
392 /// std::cout << std::endl;
396 #include "backgroundmanager.moc"