Kstars

thumbnailpicker.cpp
1/*
2 SPDX-FileCopyrightText: 2005 Jason Harris <kstars@30doradus.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "thumbnailpicker.h"
8
9#include "kstarsdata.h"
10#include "ksutils.h"
11#include "ksnotification.h"
12#include "skyobjectuserdata.h"
13#include "thumbnaileditor.h"
14#include "dialogs/detaildialog.h"
15#include "skyobjects/skyobject.h"
16
17#include <KIO/CopyJob>
18#include <KMessageBox>
19#include <KJobUiDelegate>
20
21#include <QDebug>
22
23#include <QLineEdit>
24#include <QPainter>
25#include <QPointer>
26#include <QScreen>
27#include <QUrlQuery>
28
29ThumbnailPickerUI::ThumbnailPickerUI(QWidget *parent) : QFrame(parent)
30{
31 setupUi(this);
32}
33
34ThumbnailPicker::ThumbnailPicker(SkyObject *o, const QPixmap &current, QWidget *parent, double _w, double _h,
36 : QDialog(parent), SelectedImageIndex(-1), Object(o), bImageFound(false)
37{
38#ifdef Q_OS_OSX
39 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
40#endif
41 thumbWidth = _w;
42 thumbHeight = _h;
44 ImageRect = new QRect(0, 0, 200, 200);
45
46 ui = new ThumbnailPickerUI(this);
47
48 setWindowTitle(cap);
49
50 QVBoxLayout *mainLayout = new QVBoxLayout;
51 mainLayout->addWidget(ui);
52 setLayout(mainLayout);
53
55 mainLayout->addWidget(buttonBox);
56 connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
57 connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
58
59 ui->CurrentImage->setPixmap(*Image);
60
61 connect(ui->EditButton, SIGNAL(clicked()), this, SLOT(slotEditImage()));
62 connect(ui->UnsetButton, SIGNAL(clicked()), this, SLOT(slotUnsetImage()));
63 connect(ui->ImageList, SIGNAL(currentRowChanged(int)), this, SLOT(slotSetFromList(int)));
64 connect(ui->ImageURLBox, SIGNAL(urlSelected(QUrl)), this, SLOT(slotSetFromURL()));
65 connect(ui->ImageURLBox, SIGNAL(returnPressed()), this, SLOT(slotSetFromURL()));
66
67 //ui->ImageURLBox->lineEdit()->setTrapReturnKey( true );
68 ui->EditButton->setEnabled(false);
69
70 slotFillList();
71}
72
73ThumbnailPicker::~ThumbnailPicker()
74{
75 while (!PixList.isEmpty())
76 delete PixList.takeFirst();
77}
78
79//Query online sources for images of the object
80void ThumbnailPicker::slotFillList()
81{
82 //Query Google Image Search:
83
84 //Search for the primary name, or longname and primary name
85 QString sName = QString("%1 ").arg(Object->name());
86 if (Object->longname() != Object->name())
87 {
88 sName = QString("%1 ").arg(Object->longname()) + sName;
89 }
91 QString("http://www.google.com/search?q=%1&tbs=itp:photo,isz:ex,iszw:200,iszh:200&tbm=isch&source=lnt")
92 .arg(sName);
93 QUrlQuery gURL(query);
94
95 //gURL.addQueryItem( "q", sName ); //add the Google-image query string
96
97 parseGooglePage(gURL.query());
98}
99
100void ThumbnailPicker::slotProcessGoogleResult(KJob *result)
101{
102 //Preload ImageList with the URLs in the object's ImageList:
103 SkyObjectUserdata::LinkList ImageList{
104 KStarsData::Instance()->getUserData(Object->name()).images()
105 };
106
107 if (result->error())
108 {
109 result->uiDelegate()->showErrorMessage();
110 result->kill();
111 return;
112 }
113
114 QString PageHTML(static_cast<KIO::StoredTransferJob *>(result)->data());
115
116 int index = PageHTML.indexOf("src=\"http:", 0);
117 while (index >= 0)
118 {
119 index += 5; //move to end of "src=\"http:" marker
120
121 //Image URL is everything from index to next occurrence of "\""
123 "", QUrl{ PageHTML.mid(index, PageHTML.indexOf("\"", index) - index) },
124 SkyObjectUserdata::Type::website });
125
126 index = PageHTML.indexOf("src=\"http:", index);
127 }
128
129 //Total Number of images to be loaded:
130 int nImages = ImageList.size();
131 if (nImages)
132 {
133 ui->SearchProgress->setMinimum(0);
134 ui->SearchProgress->setMaximum(nImages - 1);
135 ui->SearchLabel->setText(i18n("Loading images..."));
136 }
137 else
138 {
139 close();
140 return;
141 }
142
143 //Add images from the ImageList
144 for (const auto &image : ImageList)
145 {
146 const QUrl &u{ image.url };
147
148 if (u.isValid())
149 {
151 KIO::storedGet(u, KIO::NoReload, KIO::HideProgressInfo);
152 j->setUiDelegate(nullptr);
153 connect(j, SIGNAL(result(KJob *)), SLOT(slotJobResult(KJob *)));
154 }
155 }
156}
157
158void ThumbnailPicker::slotJobResult(KJob *job)
159{
161
162 //Update Progressbar
163 if (!ui->SearchProgress->isHidden())
164 {
165 ui->SearchProgress->setValue(ui->SearchProgress->value() + 1);
166 if (ui->SearchProgress->value() == ui->SearchProgress->maximum())
167 {
168 ui->SearchProgress->hide();
169 ui->SearchLabel->setText(i18n("Search results:"));
170 }
171 }
172
173 //If there was a problem, just return silently without adding image to list.
174 if (job->error())
175 {
176 qDebug() << Q_FUNC_INFO << " error=" << job->error();
177 job->kill();
178 return;
179 }
180
181 QPixmap *pm = new QPixmap();
182 pm->loadFromData(stjob->data());
183
184 uint w = pm->width();
185 uint h = pm->height();
186 uint pad =
187 0; /*FIXME LATER 4* QDialogBase::marginHint() + 2*ui->SearchLabel->height() + QDialogBase::actionButton( QDialogBase::Ok )->height() + 25;*/
188 uint hDesk = QGuiApplication::primaryScreen()->geometry().height() - pad;
189
190 if (h > hDesk)
192
193 PixList.append(pm);
194
195 //Add 50x50 image and URL to listbox
196 //ui->ImageList->insertItem( shrinkImage( PixList.last(), 50 ),
197 // cjob->srcURLs().first().prettyUrl() );
198 ui->ImageList->addItem(new QListWidgetItem(QIcon(shrinkImage(PixList.last(), 200)), stjob->url().url()));
199}
200
201//void ThumbnailPicker::parseGooglePage( QStringList &ImList, const QString &URL )
202void ThumbnailPicker::parseGooglePage(const QString &URL)
203{
206 connect(job, SIGNAL(result(KJob*)), this, SLOT(slotProcessGoogleResult(KJob*)));
207
208 job->start();
209}
210
211QPixmap ThumbnailPicker::shrinkImage(QPixmap *pm, int size, bool setImage)
212{
213 int w(pm->width()), h(pm->height());
214 int bigSize(w);
215 int rx(0), ry(0), sx(0), sy(0), bx(0), by(0);
216 if (size == 0)
217 return QPixmap();
218
219 //Prepare variables for rescaling image (if it is larger than 'size')
220 if (w > size && w >= h)
221 {
222 h = size;
223 w = size * pm->width() / pm->height();
224 }
225 else if (h > size && h > w)
226 {
227 w = size;
228 h = size * pm->height() / pm->width();
229 }
230 sx = (w - size) / 2;
231 sy = (h - size) / 2;
232 if (sx < 0)
233 {
234 rx = -sx;
235 sx = 0;
236 }
237 if (sy < 0)
238 {
239 ry = -sy;
240 sy = 0;
241 }
242
243 if (setImage)
244 bigSize = int(200. * float(pm->width()) / float(w));
245
247 result.fill(Qt::black); //in case final image is smaller than 'size'
248
249 if (pm->width() > size || pm->height() > size) //image larger than 'size'?
250 {
251 //convert to QImage so we can smoothscale it
252 QImage im(pm->toImage());
254
255 //bitBlt sizexsize square section of image
256 QPainter p;
257 p.begin(&result);
258 p.drawImage(rx, ry, im, sx, sy, size, size);
259 p.end();
260
261 if (setImage)
262 {
263 bx = int(sx * float(pm->width()) / float(w));
264 by = int(sy * float(pm->width()) / float(w));
265 ImageRect->setRect(bx, by, bigSize, bigSize);
266 }
267 }
268 else //image is smaller than size x size
269 {
270 QPainter p;
271 p.begin(&result);
272 p.drawImage(rx, ry, pm->toImage());
273 p.end();
274
275 if (setImage)
276 {
277 bx = int(rx * float(pm->width()) / float(w));
278 by = int(ry * float(pm->width()) / float(w));
279 ImageRect->setRect(bx, by, bigSize, bigSize);
280 }
281 }
282
283 return result;
284}
285
286void ThumbnailPicker::slotEditImage()
287{
288 QPointer<ThumbnailEditor> te = new ThumbnailEditor(this, thumbWidth, thumbHeight);
289 if (te->exec() == QDialog::Accepted)
290 {
291 QPixmap pm = te->thumbnail();
292 *Image = pm;
293 ui->CurrentImage->setPixmap(pm);
294 ui->CurrentImage->update();
295 }
296 delete te;
297}
298
299void ThumbnailPicker::slotUnsetImage()
300{
301 // QFile file;
302 //if ( KSUtils::openDataFile( file, "noimage.png" ) ) {
303 // file.close();
304 // Image->load( file.fileName(), "PNG" );
305 // } else {
306 // *Image = Image->scaled( dd->thumbnail()->width(), dd->thumbnail()->height() );
307 // Image->fill( dd->palette().color( QPalette::Window ) );
308 // }
309
310 QPixmap noImage;
311 noImage.load(":/images/noimage.png");
312 Image = new QPixmap(noImage.scaled(thumbWidth, thumbHeight, Qt::KeepAspectRatio, Qt::FastTransformation));
313
314 ui->EditButton->setEnabled(false);
315 ui->CurrentImage->setPixmap(*Image);
316 ui->CurrentImage->update();
317
318 bImageFound = false;
319}
320
321void ThumbnailPicker::slotSetFromList(int i)
322{
323 //Display image in preview pane
324 QPixmap pm;
325 pm = shrinkImage(PixList[i], 200, true); //scale image
326 SelectedImageIndex = i;
327
328 ui->CurrentImage->setPixmap(pm);
329 ui->CurrentImage->update();
330 ui->EditButton->setEnabled(true);
331
332 *Image = PixList[i]->scaled(thumbWidth, thumbHeight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
333 bImageFound = true;
334}
335
336void ThumbnailPicker::slotSetFromURL()
337{
338 //Attempt to load the specified URL
339 QUrl u = ui->ImageURLBox->url();
340
341 if (u.isValid())
342 {
343 if (u.isLocalFile())
344 {
345 QFile localFile(u.toLocalFile());
346
347 //Add image to list
348 //If image is taller than desktop, rescale it.
349 QImage im(localFile.fileName());
350
351 if (im.isNull())
352 {
353 KSNotification::sorry(i18n("Failed to load image at %1", localFile.fileName()), i18n("Failed to load image"));
354 return;
355 }
356
357 uint w = im.width();
358 uint h = im.height();
359 uint pad =
360 0; /* FIXME later 4*marginHint() + 2*ui->SearchLabel->height() + actionButton( Ok )->height() + 25; */
361 uint hDesk = QGuiApplication::primaryScreen()->geometry().height() - pad;
362
363 if (h > hDesk)
365
366 //Add Image to top of list and 50x50 thumbnail image and URL to top of listbox
367 PixList.insert(0, new QPixmap(QPixmap::fromImage(im)));
368 ui->ImageList->insertItem(0, new QListWidgetItem(QIcon(shrinkImage(PixList.last(), 50)), u.url()));
369
370 //Select the new image
371 ui->ImageList->setCurrentRow(0);
372 slotSetFromList(0);
373 }
374 else
375 {
377 j->setUiDelegate(nullptr);
378 connect(j, SIGNAL(result(KJob*)), SLOT(slotJobResult(KJob*)));
379 }
380 }
381}
int error() const
virtual Q_SCRIPTABLE void start()=0
bool kill(KJob::KillVerbosity verbosity=KJob::Quietly)
Provides all necessary information about an object in the sky: its coordinates, name(s),...
Definition skyobject.h:42
virtual QString name(void) const
Definition skyobject.h:145
virtual QString longname(void) const
Definition skyobject.h:164
QString i18n(const char *text, const TYPE &arg...)
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
KIOCORE_EXPORT StoredTransferJob * storedGet(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
HideProgressInfo
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
int result() const const
void append(QList< T > &&value)
iterator insert(const_iterator before, parameter_type value)
bool isEmpty() const const
T & last()
value_type takeFirst()
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool begin(QPaintDevice *device)
void drawImage(const QPoint &point, const QImage &image)
bool end()
QPixmap fromImage(QImage &&image, Qt::ImageConversionFlags flags)
bool load(const QString &fileName, const char *format, Qt::ImageConversionFlags flags)
QPixmap scaled(const QSize &size, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
void setRect(int x, int y, int width, int height)
QString arg(Args &&... args) const const
KeepAspectRatio
FastTransformation
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool close()
void setEnabled(bool)
void hide()
bool isHidden() const const
void update()
Stores the tite and URL of a webpage.
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:02 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.