File indexing completed on 2025-11-09 04:11:26
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2008-05-19 0007 * Description : Fuzzy search sidebar tab contents - similar panel. 0008 * 0009 * SPDX-FileCopyrightText: 2016-2018 by Mario Frank <mario dot frank at uni minus potsdam dot de> 0010 * SPDX-FileCopyrightText: 2008-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * SPDX-FileCopyrightText: 2008-2012 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0012 * SPDX-FileCopyrightText: 2012 by Andi Clemens <andi dot clemens at gmail dot com> 0013 * 0014 * SPDX-License-Identifier: GPL-2.0-or-later 0015 * 0016 * ============================================================ */ 0017 0018 #include "fuzzysearchview_p.h" 0019 0020 namespace Digikam 0021 { 0022 0023 QWidget* FuzzySearchView::setupFindSimilarPanel() const 0024 { 0025 const int spacing = qMin(QApplication::style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing), 0026 QApplication::style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing)); 0027 0028 DHBox* const imageBox = new DHBox(); 0029 d->imageWidget = new QLabel(imageBox); 0030 d->imageWidget->setFixedSize(256, 256); 0031 d->imageWidget->setText(i18n("<p>Drag & drop an image here<br/>to perform similar<br/>items search.</p>" 0032 "<p>You can also use the context menu<br/> when browsing through your images.</p>")); 0033 d->imageWidget->setAlignment(Qt::AlignCenter); 0034 imageBox->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); 0035 imageBox->setLineWidth(1); 0036 0037 // --------------------------------------------------------------- 0038 0039 QLabel* const file = new QLabel(i18n("<b>File</b>:")); 0040 d->labelFile = new DAdjustableLabel(nullptr); 0041 QLabel* const folder = new QLabel(i18n("<b>Folder</b>:")); 0042 d->labelFolder = new DAdjustableLabel(nullptr); 0043 int hgt = fontMetrics().height() - 2; 0044 file->setMaximumHeight(hgt); 0045 folder->setMaximumHeight(hgt); 0046 d->labelFile->setMaximumHeight(hgt); 0047 d->labelFolder->setMaximumHeight(hgt); 0048 0049 // --------------------------------------------------------------- 0050 0051 d->fuzzySearchAlbumSelectors = new AlbumSelectors(i18nc("@label", "Search in albums:"), 0052 QLatin1String("Fuzzy Search View"), 0053 nullptr, AlbumSelectors::AlbumType::PhysAlbum, true); 0054 0055 // --------------------------------------------------------------- 0056 0057 QLabel* const resultsLabel = new QLabel(i18n("Similarity range:")); 0058 d->similarityRange = new DIntRangeBox(); 0059 d->similarityRange->setSuffix(QLatin1String("%")); 0060 0061 if (d->settings) 0062 { 0063 d->similarityRange->setRange(d->settings->getMinimumSimilarityBound(), 100); 0064 d->similarityRange->setInterval(d->settings->getDuplicatesSearchLastMinSimilarity(), 0065 d->settings->getDuplicatesSearchLastMaxSimilarity()); 0066 } 0067 else 0068 { 0069 d->similarityRange->setRange(40, 100); 0070 d->similarityRange->setInterval(90, 100); 0071 } 0072 0073 d->similarityRange->setWhatsThis(i18n("Select here the approximate similarity interval " 0074 "as a percentage. ")); 0075 0076 // --------------------------------------------------------------- 0077 0078 DHBox* const saveBox = new DHBox(); 0079 saveBox->setContentsMargins(QMargins()); 0080 saveBox->setSpacing(spacing); 0081 0082 d->nameEditImage = new DTextEdit(saveBox); 0083 d->nameEditImage->setLinesVisible(1); 0084 d->nameEditImage->setWhatsThis(i18n("Enter the name of the current similar image search to save in the " 0085 "\"Similarity Searches\" view.")); 0086 0087 d->saveBtnImage = new QToolButton(saveBox); 0088 d->saveBtnImage->setIcon(QIcon::fromTheme(QLatin1String("document-save"))); 0089 d->saveBtnImage->setEnabled(false); 0090 d->saveBtnImage->setToolTip(i18n("Save current similar image search to a new virtual Album")); 0091 d->saveBtnImage->setWhatsThis(i18n("If you press this button, the current " 0092 "similar image search will be saved to a new search " 0093 "virtual album using name " 0094 "set on the left side.")); 0095 0096 // --------------------------------------------------------------- 0097 0098 QWidget* const mainWidget = new QWidget(); 0099 QGridLayout* const mainLayout = new QGridLayout(); 0100 mainLayout->addWidget(imageBox, 0, 0, 1, 6); 0101 mainLayout->addWidget(file, 1, 0, 1, 1); 0102 mainLayout->addWidget(d->labelFile, 1, 1, 1, 5); 0103 mainLayout->addWidget(folder, 2, 0, 1, 1); 0104 mainLayout->addWidget(d->labelFolder, 2, 1, 1, 5); 0105 mainLayout->addWidget(d->fuzzySearchAlbumSelectors, 3, 0, 1, -1); 0106 mainLayout->addWidget(resultsLabel, 4, 0, 1, 1); 0107 mainLayout->addWidget(d->similarityRange, 4, 2, 1, 1); 0108 mainLayout->addWidget(saveBox, 5, 0, 1, 6); 0109 mainLayout->setRowStretch(0, 10); 0110 mainLayout->setColumnStretch(1, 10); 0111 mainLayout->setContentsMargins(spacing, spacing, spacing, spacing); 0112 mainLayout->setSpacing(spacing); 0113 mainWidget->setLayout(mainLayout); 0114 0115 return mainWidget; 0116 } 0117 0118 void FuzzySearchView::dragEnterEvent(QDragEnterEvent* e) 0119 { 0120 if (dragEventWrapper(e->mimeData())) 0121 { 0122 e->acceptProposedAction(); 0123 } 0124 } 0125 0126 void FuzzySearchView::dragMoveEvent(QDragMoveEvent* e) 0127 { 0128 if (dragEventWrapper(e->mimeData())) 0129 { 0130 e->acceptProposedAction(); 0131 } 0132 } 0133 0134 bool FuzzySearchView::dragEventWrapper(const QMimeData* data) const 0135 { 0136 if (DItemDrag::canDecode(data)) 0137 { 0138 return true; 0139 } 0140 else if (data->hasUrls()) 0141 { 0142 QList<QUrl> urls = data->urls(); 0143 0144 // If there is at least one URL and the URL is a local file. 0145 0146 if (!urls.isEmpty() && urls.first().isLocalFile()) 0147 { 0148 HaarIface haarIface; 0149 QString path = urls.first().toLocalFile(); 0150 const QImage image = haarIface.loadQImage(path); 0151 0152 if (!image.isNull()) 0153 { 0154 return true; 0155 } 0156 } 0157 } 0158 0159 return false; 0160 } 0161 0162 void FuzzySearchView::dropEvent(QDropEvent* e) 0163 { 0164 if (DItemDrag::canDecode(e->mimeData())) 0165 { 0166 QList<QUrl> urls; 0167 QList<int> albumIDs; 0168 QList<qlonglong> imageIDs; 0169 0170 if (!DItemDrag::decode(e->mimeData(), urls, albumIDs, imageIDs)) 0171 { 0172 return; 0173 } 0174 0175 if (imageIDs.isEmpty()) 0176 { 0177 return; 0178 } 0179 0180 setItemInfo(ItemInfo(imageIDs.first())); 0181 0182 e->acceptProposedAction(); 0183 } 0184 0185 // Allow dropping urls and handle them as sketch search if the urls represent images. 0186 0187 if (e->mimeData()->hasUrls()) 0188 { 0189 QList<QUrl> urls = e->mimeData()->urls(); 0190 0191 // If there is at least one URL and the URL is a local file. 0192 0193 if (!urls.isEmpty() && urls.first().isLocalFile()) 0194 { 0195 HaarIface haarIface; 0196 QString path = urls.first().toLocalFile(); 0197 const QImage image = haarIface.loadQImage(path); 0198 0199 if (!image.isNull()) 0200 { 0201 // Set a temporary image id 0202 0203 d->imageInfo = ItemInfo(-1); 0204 d->imageUrl = urls.first(); 0205 0206 d->imageWidget->setPixmap(QPixmap::fromImage(image).scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation)); 0207 0208 AlbumManager::instance()->clearCurrentAlbums(); 0209 QString haarTitle = SAlbum::getTemporaryHaarTitle(DatabaseSearch::HaarImageSearch); 0210 0211 QList<int> albums = d->fuzzySearchAlbumSelectors->selectedAlbumIds(); 0212 0213 d->imageSAlbum = d->searchModificationHelper->createFuzzySearchFromDropped(haarTitle, path, 0214 d->similarityRange->minValue() / 100.0, 0215 d->similarityRange->maxValue() / 100.0, 0216 albums, true); 0217 d->searchTreeView->setCurrentAlbums(QList<Album*>() << d->imageSAlbum); 0218 d->labelFile->setAdjustedText(urls.first().fileName()); 0219 d->labelFolder->setAdjustedText(urls.first().adjusted(QUrl::RemoveFilename).toLocalFile()); 0220 0221 slotCheckNameEditImageConditions(); 0222 0223 e->acceptProposedAction(); 0224 } 0225 } 0226 } 0227 } 0228 0229 void FuzzySearchView::slotMaxLevelImageChanged(int /*newValue*/) 0230 { 0231 if (d->active) 0232 { 0233 d->timerImage->start(); 0234 } 0235 } 0236 0237 void FuzzySearchView::slotMinLevelImageChanged(int /*newValue*/) 0238 { 0239 if (d->active) 0240 { 0241 d->timerImage->start(); 0242 } 0243 } 0244 0245 void FuzzySearchView::slotFuzzyAlbumsChanged() 0246 { 0247 if (d->active) 0248 { 0249 d->timerImage->start(); 0250 } 0251 } 0252 0253 void FuzzySearchView::slotTimerImageDone() 0254 { 0255 if (d->imageInfo.isNull() && (d->imageInfo.id() == -1) && !d->imageUrl.isEmpty()) 0256 { 0257 AlbumManager::instance()->clearCurrentAlbums(); 0258 QString haarTitle = SAlbum::getTemporaryHaarTitle(DatabaseSearch::HaarImageSearch); 0259 0260 QList<int> albums = d->fuzzySearchAlbumSelectors->selectedAlbumIds(); 0261 0262 d->imageSAlbum = d->searchModificationHelper->createFuzzySearchFromDropped(haarTitle, 0263 d->imageUrl.toLocalFile(), 0264 d->similarityRange->minValue() / 100.0, 0265 d->similarityRange->maxValue() / 100.0, 0266 albums, true); 0267 d->searchTreeView->setCurrentAlbums(QList<Album*>() << d->imageSAlbum); 0268 return; 0269 } 0270 0271 if (!d->imageInfo.isNull() && d->active) 0272 { 0273 setItemInfo(d->imageInfo); 0274 } 0275 } 0276 0277 void FuzzySearchView::setCurrentImage(qlonglong imageid) 0278 { 0279 setCurrentImage(ItemInfo(imageid)); 0280 } 0281 0282 void FuzzySearchView::setCurrentImage(const ItemInfo& info) 0283 { 0284 d->imageInfo = info; 0285 d->imageUrl = info.fileUrl(); 0286 d->labelFile->setAdjustedText(d->imageInfo.name()); 0287 d->labelFolder->setAdjustedText(d->imageInfo.fileUrl().adjusted(QUrl::RemoveFilename).toLocalFile()); 0288 d->thumbLoadThread->find(d->imageInfo.thumbnailIdentifier()); 0289 } 0290 0291 void FuzzySearchView::setItemInfo(const ItemInfo& info) 0292 { 0293 setCurrentImage(info); 0294 slotCheckNameEditImageConditions(); 0295 createNewFuzzySearchAlbumFromImage(SAlbum::getTemporaryHaarTitle(DatabaseSearch::HaarImageSearch), true); 0296 d->tabWidget->setCurrentIndex((int)Private::SIMILARS); 0297 } 0298 0299 void FuzzySearchView::slotThumbnailLoaded(const LoadingDescription& desc, const QPixmap& pix) 0300 { 0301 if (!d->imageInfo.isNull() && (QUrl::fromLocalFile(desc.filePath) == d->imageInfo.fileUrl())) 0302 { 0303 d->imageWidget->setPixmap(pix.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation)); 0304 } 0305 } 0306 0307 void FuzzySearchView::createNewFuzzySearchAlbumFromImage(const QString& name, bool force) 0308 { 0309 AlbumManager::instance()->clearCurrentAlbums(); 0310 QList<int> albums = d->fuzzySearchAlbumSelectors->selectedAlbumIds(); 0311 0312 d->imageSAlbum = d->searchModificationHelper->createFuzzySearchFromImage(name, d->imageInfo, 0313 d->similarityRange->minValue() / 100.0, 0314 d->similarityRange->maxValue() / 100.0, 0315 albums, force); 0316 d->searchTreeView->setCurrentAlbums(QList<Album*>() << d->imageSAlbum); 0317 } 0318 0319 void FuzzySearchView::slotCheckNameEditImageConditions() 0320 { 0321 if (!d->imageInfo.isNull()) 0322 { 0323 bool b = d->nameEditImage->text().isEmpty(); 0324 d->nameEditImage->setEnabled(true); 0325 d->saveBtnImage->setEnabled(!b); 0326 } 0327 else 0328 { 0329 d->nameEditImage->setEnabled(false); 0330 d->saveBtnImage->setEnabled(false); 0331 } 0332 } 0333 0334 void FuzzySearchView::slotSaveImageSAlbum() 0335 { 0336 createNewFuzzySearchAlbumFromImage(d->nameEditImage->text()); 0337 } 0338 0339 } // namespace Digikam