File indexing completed on 2024-04-14 14:09:17

0001 /*
0002     SPDX-FileCopyrightText: 2005 Jason Harris <kstars@30doradus.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "thumbnailpicker.h"
0008 
0009 #include "kstarsdata.h"
0010 #include "ksutils.h"
0011 #include "ksnotification.h"
0012 #include "skyobjectuserdata.h"
0013 #include "thumbnaileditor.h"
0014 #include "dialogs/detaildialog.h"
0015 #include "skyobjects/skyobject.h"
0016 
0017 #include <KIO/CopyJob>
0018 #include <KMessageBox>
0019 #include <KJobUiDelegate>
0020 
0021 #include <QDebug>
0022 
0023 #include <QLineEdit>
0024 #include <QPainter>
0025 #include <QPointer>
0026 #include <QScreen>
0027 #include <QUrlQuery>
0028 
0029 ThumbnailPickerUI::ThumbnailPickerUI(QWidget *parent) : QFrame(parent)
0030 {
0031     setupUi(this);
0032 }
0033 
0034 ThumbnailPicker::ThumbnailPicker(SkyObject *o, const QPixmap &current, QWidget *parent, double _w, double _h,
0035                                  QString cap)
0036     : QDialog(parent), SelectedImageIndex(-1), Object(o), bImageFound(false)
0037 {
0038 #ifdef Q_OS_OSX
0039     setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
0040 #endif
0041     thumbWidth  = _w;
0042     thumbHeight = _h;
0043     Image       = new QPixmap(current.scaled(_w, _h, Qt::KeepAspectRatio, Qt::FastTransformation));
0044     ImageRect   = new QRect(0, 0, 200, 200);
0045 
0046     ui = new ThumbnailPickerUI(this);
0047 
0048     setWindowTitle(cap);
0049 
0050     QVBoxLayout *mainLayout = new QVBoxLayout;
0051     mainLayout->addWidget(ui);
0052     setLayout(mainLayout);
0053 
0054     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
0055     mainLayout->addWidget(buttonBox);
0056     connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
0057     connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
0058 
0059     ui->CurrentImage->setPixmap(*Image);
0060 
0061     connect(ui->EditButton, SIGNAL(clicked()), this, SLOT(slotEditImage()));
0062     connect(ui->UnsetButton, SIGNAL(clicked()), this, SLOT(slotUnsetImage()));
0063     connect(ui->ImageList, SIGNAL(currentRowChanged(int)), this, SLOT(slotSetFromList(int)));
0064     connect(ui->ImageURLBox, SIGNAL(urlSelected(QUrl)), this, SLOT(slotSetFromURL()));
0065     connect(ui->ImageURLBox, SIGNAL(returnPressed()), this, SLOT(slotSetFromURL()));
0066 
0067     //ui->ImageURLBox->lineEdit()->setTrapReturnKey( true );
0068     ui->EditButton->setEnabled(false);
0069 
0070     slotFillList();
0071 }
0072 
0073 ThumbnailPicker::~ThumbnailPicker()
0074 {
0075     while (!PixList.isEmpty())
0076         delete PixList.takeFirst();
0077 }
0078 
0079 //Query online sources for images of the object
0080 void ThumbnailPicker::slotFillList()
0081 {
0082     //Query Google Image Search:
0083 
0084     //Search for the primary name, or longname and primary name
0085     QString sName = QString("%1 ").arg(Object->name());
0086     if (Object->longname() != Object->name())
0087     {
0088         sName = QString("%1 ").arg(Object->longname()) + sName;
0089     }
0090     QString query =
0091         QString("http://www.google.com/search?q=%1&tbs=itp:photo,isz:ex,iszw:200,iszh:200&tbm=isch&source=lnt")
0092         .arg(sName);
0093     QUrlQuery gURL(query);
0094 
0095     //gURL.addQueryItem( "q", sName ); //add the Google-image query string
0096 
0097     parseGooglePage(gURL.query());
0098 }
0099 
0100 void ThumbnailPicker::slotProcessGoogleResult(KJob *result)
0101 {
0102     //Preload ImageList with the URLs in the object's ImageList:
0103     SkyObjectUserdata::LinkList ImageList{
0104         KStarsData::Instance()->getUserData(Object->name()).images()
0105     };
0106 
0107     if (result->error())
0108     {
0109         result->uiDelegate()->showErrorMessage();
0110         result->kill();
0111         return;
0112     }
0113 
0114     QString PageHTML(static_cast<KIO::StoredTransferJob *>(result)->data());
0115 
0116     int index = PageHTML.indexOf("src=\"http:", 0);
0117     while (index >= 0)
0118     {
0119         index += 5; //move to end of "src=\"http:" marker
0120 
0121         //Image URL is everything from index to next occurrence of "\""
0122         ImageList.push_back(SkyObjectUserdata::LinkData{
0123             "", QUrl{ PageHTML.mid(index, PageHTML.indexOf("\"", index) - index) },
0124             SkyObjectUserdata::Type::website });
0125 
0126         index = PageHTML.indexOf("src=\"http:", index);
0127     }
0128 
0129     //Total Number of images to be loaded:
0130     int nImages = ImageList.size();
0131     if (nImages)
0132     {
0133         ui->SearchProgress->setMinimum(0);
0134         ui->SearchProgress->setMaximum(nImages - 1);
0135         ui->SearchLabel->setText(i18n("Loading images..."));
0136     }
0137     else
0138     {
0139         close();
0140         return;
0141     }
0142 
0143     //Add images from the ImageList
0144     for (const auto &image : ImageList)
0145     {
0146         const QUrl &u{ image.url };
0147 
0148         if (u.isValid())
0149         {
0150             KIO::StoredTransferJob *j =
0151                 KIO::storedGet(u, KIO::NoReload, KIO::HideProgressInfo);
0152             j->setUiDelegate(nullptr);
0153             connect(j, SIGNAL(result(KJob *)), SLOT(slotJobResult(KJob *)));
0154         }
0155     }
0156 }
0157 
0158 void ThumbnailPicker::slotJobResult(KJob *job)
0159 {
0160     KIO::StoredTransferJob *stjob = (KIO::StoredTransferJob *)job;
0161 
0162     //Update Progressbar
0163     if (!ui->SearchProgress->isHidden())
0164     {
0165         ui->SearchProgress->setValue(ui->SearchProgress->value() + 1);
0166         if (ui->SearchProgress->value() == ui->SearchProgress->maximum())
0167         {
0168             ui->SearchProgress->hide();
0169             ui->SearchLabel->setText(i18n("Search results:"));
0170         }
0171     }
0172 
0173     //If there was a problem, just return silently without adding image to list.
0174     if (job->error())
0175     {
0176         qDebug() << Q_FUNC_INFO << " error=" << job->error();
0177         job->kill();
0178         return;
0179     }
0180 
0181     QPixmap *pm = new QPixmap();
0182     pm->loadFromData(stjob->data());
0183 
0184     uint w = pm->width();
0185     uint h = pm->height();
0186     uint pad =
0187         0; /*FIXME LATER 4* QDialogBase::marginHint() + 2*ui->SearchLabel->height() + QDialogBase::actionButton( QDialogBase::Ok )->height() + 25;*/
0188     uint hDesk = QGuiApplication::primaryScreen()->geometry().height() - pad;
0189 
0190     if (h > hDesk)
0191         *pm = pm->scaled(w * hDesk / h, hDesk, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
0192 
0193     PixList.append(pm);
0194 
0195     //Add 50x50 image and URL to listbox
0196     //ui->ImageList->insertItem( shrinkImage( PixList.last(), 50 ),
0197     //      cjob->srcURLs().first().prettyUrl() );
0198     ui->ImageList->addItem(new QListWidgetItem(QIcon(shrinkImage(PixList.last(), 200)), stjob->url().url()));
0199 }
0200 
0201 //void ThumbnailPicker::parseGooglePage( QStringList &ImList, const QString &URL )
0202 void ThumbnailPicker::parseGooglePage(const QString &URL)
0203 {
0204     QUrl googleURL(URL);
0205     KIO::StoredTransferJob *job = KIO::storedGet(googleURL);
0206     connect(job, SIGNAL(result(KJob*)), this, SLOT(slotProcessGoogleResult(KJob*)));
0207 
0208     job->start();
0209 }
0210 
0211 QPixmap ThumbnailPicker::shrinkImage(QPixmap *pm, int size, bool setImage)
0212 {
0213     int w(pm->width()), h(pm->height());
0214     int bigSize(w);
0215     int rx(0), ry(0), sx(0), sy(0), bx(0), by(0);
0216     if (size == 0)
0217         return QPixmap();
0218 
0219     //Prepare variables for rescaling image (if it is larger than 'size')
0220     if (w > size && w >= h)
0221     {
0222         h = size;
0223         w = size * pm->width() / pm->height();
0224     }
0225     else if (h > size && h > w)
0226     {
0227         w = size;
0228         h = size * pm->height() / pm->width();
0229     }
0230     sx = (w - size) / 2;
0231     sy = (h - size) / 2;
0232     if (sx < 0)
0233     {
0234         rx = -sx;
0235         sx = 0;
0236     }
0237     if (sy < 0)
0238     {
0239         ry = -sy;
0240         sy = 0;
0241     }
0242 
0243     if (setImage)
0244         bigSize = int(200. * float(pm->width()) / float(w));
0245 
0246     QPixmap result(size, size);
0247     result.fill(Qt::black); //in case final image is smaller than 'size'
0248 
0249     if (pm->width() > size || pm->height() > size) //image larger than 'size'?
0250     {
0251         //convert to QImage so we can smoothscale it
0252         QImage im(pm->toImage());
0253         im = im.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
0254 
0255         //bitBlt sizexsize square section of image
0256         QPainter p;
0257         p.begin(&result);
0258         p.drawImage(rx, ry, im, sx, sy, size, size);
0259         p.end();
0260 
0261         if (setImage)
0262         {
0263             bx = int(sx * float(pm->width()) / float(w));
0264             by = int(sy * float(pm->width()) / float(w));
0265             ImageRect->setRect(bx, by, bigSize, bigSize);
0266         }
0267     }
0268     else //image is smaller than size x size
0269     {
0270         QPainter p;
0271         p.begin(&result);
0272         p.drawImage(rx, ry, pm->toImage());
0273         p.end();
0274 
0275         if (setImage)
0276         {
0277             bx = int(rx * float(pm->width()) / float(w));
0278             by = int(ry * float(pm->width()) / float(w));
0279             ImageRect->setRect(bx, by, bigSize, bigSize);
0280         }
0281     }
0282 
0283     return result;
0284 }
0285 
0286 void ThumbnailPicker::slotEditImage()
0287 {
0288     QPointer<ThumbnailEditor> te = new ThumbnailEditor(this, thumbWidth, thumbHeight);
0289     if (te->exec() == QDialog::Accepted)
0290     {
0291         QPixmap pm = te->thumbnail();
0292         *Image     = pm;
0293         ui->CurrentImage->setPixmap(pm);
0294         ui->CurrentImage->update();
0295     }
0296     delete te;
0297 }
0298 
0299 void ThumbnailPicker::slotUnsetImage()
0300 {
0301     //  QFile file;
0302     //if ( KSUtils::openDataFile( file, "noimage.png" ) ) {
0303     //    file.close();
0304     //     Image->load( file.fileName(), "PNG" );
0305     // } else {
0306     //     *Image = Image->scaled( dd->thumbnail()->width(), dd->thumbnail()->height() );
0307     //      Image->fill( dd->palette().color( QPalette::Window ) );
0308     //  }
0309 
0310     QPixmap noImage;
0311     noImage.load(":/images/noimage.png");
0312     Image = new QPixmap(noImage.scaled(thumbWidth, thumbHeight, Qt::KeepAspectRatio, Qt::FastTransformation));
0313 
0314     ui->EditButton->setEnabled(false);
0315     ui->CurrentImage->setPixmap(*Image);
0316     ui->CurrentImage->update();
0317 
0318     bImageFound = false;
0319 }
0320 
0321 void ThumbnailPicker::slotSetFromList(int i)
0322 {
0323     //Display image in preview pane
0324     QPixmap pm;
0325     pm                 = shrinkImage(PixList[i], 200, true); //scale image
0326     SelectedImageIndex = i;
0327 
0328     ui->CurrentImage->setPixmap(pm);
0329     ui->CurrentImage->update();
0330     ui->EditButton->setEnabled(true);
0331 
0332     *Image      = PixList[i]->scaled(thumbWidth, thumbHeight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
0333     bImageFound = true;
0334 }
0335 
0336 void ThumbnailPicker::slotSetFromURL()
0337 {
0338     //Attempt to load the specified URL
0339     QUrl u = ui->ImageURLBox->url();
0340 
0341     if (u.isValid())
0342     {
0343         if (u.isLocalFile())
0344         {
0345             QFile localFile(u.toLocalFile());
0346 
0347             //Add image to list
0348             //If image is taller than desktop, rescale it.
0349             QImage im(localFile.fileName());
0350 
0351             if (im.isNull())
0352             {
0353                 KSNotification::sorry(i18n("Failed to load image at %1", localFile.fileName()), i18n("Failed to load image"));
0354                 return;
0355             }
0356 
0357             uint w = im.width();
0358             uint h = im.height();
0359             uint pad =
0360                 0; /* FIXME later 4*marginHint() + 2*ui->SearchLabel->height() + actionButton( Ok )->height() + 25; */
0361             uint hDesk = QGuiApplication::primaryScreen()->geometry().height() - pad;
0362 
0363             if (h > hDesk)
0364                 im = im.scaled(w * hDesk / h, hDesk, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
0365 
0366             //Add Image to top of list and 50x50 thumbnail image and URL to top of listbox
0367             PixList.insert(0, new QPixmap(QPixmap::fromImage(im)));
0368             ui->ImageList->insertItem(0, new QListWidgetItem(QIcon(shrinkImage(PixList.last(), 50)), u.url()));
0369 
0370             //Select the new image
0371             ui->ImageList->setCurrentRow(0);
0372             slotSetFromList(0);
0373         }
0374         else
0375         {
0376             KIO::StoredTransferJob *j = KIO::storedGet(u, KIO::NoReload, KIO::HideProgressInfo);
0377             j->setUiDelegate(nullptr);
0378             connect(j, SIGNAL(result(KJob*)), SLOT(slotJobResult(KJob*)));
0379         }
0380     }
0381 }