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

KDE's Doxygen guidelines are available online.