Kstars

thumbnailpicker.cpp
1 /*
2  SPDX-FileCopyrightText: 2005 Jason Harris <[email protected]>
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 
29 ThumbnailPickerUI::ThumbnailPickerUI(QWidget *parent) : QFrame(parent)
30 {
31  setupUi(this);
32 }
33 
34 ThumbnailPicker::ThumbnailPicker(SkyObject *o, const QPixmap &current, QWidget *parent, double _w, double _h,
35  QString cap)
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;
43  Image = new QPixmap(current.scaled(_w, _h, Qt::KeepAspectRatio, Qt::FastTransformation));
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 
73 ThumbnailPicker::~ThumbnailPicker()
74 {
75  while (!PixList.isEmpty())
76  delete PixList.takeFirst();
77 }
78 
79 //Query online sources for images of the object
80 void 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  }
90  QString query =
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 
100 void 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 "\""
122  ImageList.push_back(SkyObjectUserdata::LinkData{
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 
158 void 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)
191  *pm = pm->scaled(w * hDesk / h, hDesk, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
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 )
202 void ThumbnailPicker::parseGooglePage(const QString &URL)
203 {
204  QUrl googleURL(URL);
205  KIO::StoredTransferJob *job = KIO::storedGet(googleURL);
206  connect(job, SIGNAL(result(KJob*)), this, SLOT(slotProcessGoogleResult(KJob*)));
207 
208  job->start();
209 }
210 
211 QPixmap 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 
246  QPixmap result(size, size);
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());
253  im = im.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
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 
286 void 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 
299 void 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 
321 void 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 
336 void 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)
364  im = im.scaled(w * hDesk / h, hDesk, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
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 }
std::optional< QSqlQuery > query(const QString &queryStatement)
QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags)
virtual Q_SCRIPTABLE void start()=0
QString url(QUrl::FormattingOptions options) const const
virtual void showErrorMessage()
const QUrl & url() const
bool kill(KillVerbosity verbosity=Quietly)
const SkyObjectUserdata::Data & getUserData(const QString &name)
Get a reference to the user data of an object with the name name.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
KJobUiDelegate * uiDelegate() const
void drawImage(const QRectF &target, const QImage &image, const QRectF &source, Qt::ImageConversionFlags flags)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
bool begin(QPaintDevice *device)
bool isValid() const const
bool loadFromData(const uchar *data, uint len, const char *format, Qt::ImageConversionFlags flags)
bool end()
KeepAspectRatio
QString i18n(const char *text, const TYPE &arg...)
QAction * close(const QObject *recvr, const char *slot, QObject *parent)
QString toLocalFile() const const
int height() const const
QImage toImage() const const
QByteArray data() const
bool load(const QString &fileName, const char *format, Qt::ImageConversionFlags flags)
QPixmap scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
bool isLocalFile() const const
void setUiDelegate(KJobUiDelegate *delegate)
HideProgressInfo
int error() const
FastTransformation
KIOCORE_EXPORT StoredTransferJob * storedGet(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
Information about an object in the sky.
Definition: skyobject.h:41
int width() const const
Stores the tite and URL of a webpage.
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Tue Aug 16 2022 04:00:59 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.