2 * Copyright (c) 2007 Paolo Capriotti <p.capriotti@gmail.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2,
6 * or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details
13 * You should have received a copy of the GNU Library General Public
14 * License along with this program; if not, write to the
15 * Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 #include "backgroundpackage.h"
21 // <cmath> does not define fabs (by the standard, even if it does with gcc)
23 #include <float.h> // FLT_MAX
28 #include <KLocalizedString>
29 #include <KStandardDirs>
30 #include <KSvgRenderer>
31 #include <Plasma/PackageStructure>
32 #include <Plasma/PackageMetadata>
33 #include <ThreadWeaver/Weaver>
35 using namespace Plasma
;
37 class ResizeThread
: public ThreadWeaver::Job
40 ResizeThread(const QString
&path
, float ratio
, QObject
*parent
= 0);
41 virtual ~ResizeThread();
43 virtual void start(QPersistentModelIndex index
);
46 QImage
result() const;
47 QPersistentModelIndex
index() const;
48 bool isInitialized() const;
53 QPersistentModelIndex m_index
;
56 ResizeThread::ResizeThread(const QString
&path
, float ratio
, QObject
*parent
)
57 : ThreadWeaver::Job(parent
),
63 ResizeThread::~ResizeThread() {
66 void ResizeThread::start(QPersistentModelIndex index
)
69 ThreadWeaver::Weaver::instance()->enqueue(this);
72 bool ResizeThread::isInitialized() const
74 return m_index
.isValid();
77 void ResizeThread::run()
79 m_result
= Background::createScreenshot(m_path
, m_ratio
);
82 QImage
ResizeThread::result() const
92 QPersistentModelIndex
ResizeThread::index() const
97 Background::~Background()
101 QImage
Background::createScreenshot(const QString
&path
, float ratio
)
103 if (path
.endsWith("svg") || path
.endsWith("svgz")) {
104 KSvgRenderer
renderer(path
);
105 QImage
img(QSize(int(SCREENSHOT_HEIGHT
* ratio
), SCREENSHOT_HEIGHT
),
106 QImage::Format_ARGB32_Premultiplied
);
115 return img
.scaled(int(SCREENSHOT_HEIGHT
* ratio
),
117 Qt::KeepAspectRatio
);
120 return defaultScreenshot();
126 QImage
Background::defaultScreenshot()
128 static QImage defaultScreenshotImage
;
130 if (defaultScreenshotImage
.isNull()) {
131 QImage
img(QSize(SCREENSHOT_HEIGHT
, SCREENSHOT_HEIGHT
), QImage::Format_ARGB32_Premultiplied
);
134 p
.drawText(QRect(0, 0, SCREENSHOT_HEIGHT
, SCREENSHOT_HEIGHT
),
135 Qt::AlignHCenter
| Qt::AlignVCenter
,
136 "Preview\nnot\navailable");
137 defaultScreenshotImage
= img
;
139 return defaultScreenshotImage
;
143 class BackgroundPackageStructure
: public PackageStructure
146 BackgroundPackageStructure(QObject
*parent
= 0);
148 void addResolution(const char *res
);
151 BackgroundPackageStructure::BackgroundPackageStructure(QObject
*parent
)
152 : PackageStructure(parent
, "Background")
154 QStringList mimetypes
;
155 mimetypes
<< "image/svg" << "image/png" << "image/jpeg" << "image/jpg";
156 setDefaultMimetypes(mimetypes
);
158 addDirectoryDefinition("images", "images", i18n("Images"));
159 addFileDefinition("screenshot", "screenshot.png", i18n("Screenshot"));
160 setAllowExternalPaths(true);
165 BackgroundPackage::BackgroundPackage(const QString
&path
, float ratio
)
166 : Package(path
, KSharedPtr
<Plasma::PackageStructure
>(new BackgroundPackageStructure(this))),
172 QString
BackgroundPackage::resString(const QSize
&size
) const
174 return QString::number(size
.width()) + 'x' + QString::number(size
.height());
177 QSize
BackgroundPackage::resSize(const QString
&str
) const
179 int index
= str
.indexOf('x');
181 return QSize(str
.left(index
).toInt(),
182 str
.mid(index
+ 1).toInt());
189 QString
BackgroundPackage::findBackground(const QSize
&size
,
190 ResizeMethod method
) const
192 QStringList images
= entryList("images");
193 if (images
.empty()) {
197 //kDebug() << "wanted" << size;
199 // choose the nearest resolution
200 float best
= FLT_MAX
;
202 foreach (const QString
&entry
, images
) {
203 QSize candidate
= resSize(QFileInfo(entry
).baseName());
204 if (candidate
== QSize()) {
208 double dist
= distance(candidate
, size
, method
);
209 //kDebug() << "candidate" << candidate << "distance" << dist;
210 if (bestImage
.isNull() || dist
< best
) {
211 bestImage
= filePath("images", entry
);
213 //kDebug() << "best" << bestImage;
220 //kDebug() << "best image" << bestImage;
224 float BackgroundPackage::distance(const QSize
& size
,
225 const QSize
& desired
,
226 ResizeMethod method
) const
228 // compute difference of areas
229 float delta
= size
.width() * size
.height() -
230 desired
.width() * desired
.height();
231 // scale down to about 1.0
232 delta
/= ((desired
.width() * desired
.height())+(size
.width() * size
.height()))/2;
237 // Consider first the difference in aspect ratio,
238 // then in areas. Prefer scaling down.
239 float deltaRatio
= 1.0;
240 if (size
.height() > 0 && desired
.height() > 0) {
241 deltaRatio
= float(size
.width()) / float(size
.height()) -
242 float(desired
.width()) / float(desired
.height());
244 return fabs(deltaRatio
) * 3.0 + (delta
>= 0.0 ? delta
: -delta
+ 5.0);
247 // Difference of areas, slight preference to scale down
248 return delta
>= 0.0 ? delta
: -delta
+ 2.0;
251 // Difference in areas
256 QPixmap
BackgroundPackage::screenshot() const
258 if (m_screenshot
.isNull()) {
259 QString screenshotPath
= filePath("screenshot");
260 if (!screenshotPath
.isEmpty()) {
261 QImage img
= createScreenshot(screenshotPath
, m_ratio
);
262 m_screenshot
= QPixmap::fromImage(img
);
269 bool BackgroundPackage::screenshotGenerationStarted() const
274 void BackgroundPackage::generateScreenshot(QPersistentModelIndex
) const
278 QString
BackgroundPackage::title() const
280 Plasma::PackageMetadata md
= metadata();
281 QString title
= md
.name();
282 if (title
.isEmpty()) {
283 title
= md
.pluginName();
284 title
.replace("_", " ");
289 QString
BackgroundPackage::author() const
291 return metadata().author();
294 QString
BackgroundPackage::email() const
296 return metadata().email();
299 QString
BackgroundPackage::license() const
301 return metadata().license();
304 bool BackgroundPackage::isValid() const
306 return Package::isValid();
309 QString
BackgroundPackage::path() const
315 BackgroundFile::BackgroundFile(const QString
&file
, float ratio
)
318 , m_resizer_started(false)
322 BackgroundFile::~BackgroundFile()
326 QString
BackgroundFile::findBackground(const QSize
&,
332 QPixmap
BackgroundFile::screenshot() const
337 bool BackgroundFile::screenshotGenerationStarted() const
339 return m_resizer_started
;
342 void BackgroundFile::generateScreenshot(QPersistentModelIndex index
) const
344 ResizeThread
*resizer
= new ResizeThread(m_file
, m_ratio
);
345 connect(resizer
, SIGNAL(done(ThreadWeaver::Job
*)),
346 this, SLOT(updateScreenshot(ThreadWeaver::Job
*)));
347 m_resizer_started
= true;
348 resizer
->start(index
);
351 void BackgroundFile::updateScreenshot(ThreadWeaver::Job
*job
)
353 ResizeThread
*resizer
= static_cast<ResizeThread
*>(job
);
354 m_screenshot
= QPixmap::fromImage(resizer
->result());
355 emit
screenshotDone(resizer
->index());
356 resizer
->deleteLater();
360 QString
BackgroundFile::author() const
365 QString
BackgroundFile::title() const
367 return QFileInfo(m_file
).completeBaseName();
371 QString
BackgroundFile::email() const
377 QString
BackgroundFile::license() const
383 bool BackgroundFile::isValid() const
388 QString
BackgroundFile::path() const