File indexing completed on 2025-01-19 03:57:59
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. 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 FuzzySearchView::FuzzySearchView(SearchModel* const searchModel, 0024 SearchModificationHelper* const searchModificationHelper, 0025 QWidget* const parent) 0026 : QScrollArea (parent), 0027 StateSavingObject(this), 0028 d (new Private) 0029 { 0030 d->settings = ApplicationSettings::instance(); 0031 0032 const int spacing = qMin(QApplication::style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing), 0033 QApplication::style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing)); 0034 0035 d->thumbLoadThread = ThumbnailLoadThread::defaultThread(); 0036 d->searchModel = searchModel; 0037 d->searchModificationHelper = searchModificationHelper; 0038 0039 setWidgetResizable(true); 0040 setAttribute(Qt::WA_DeleteOnClose); 0041 setAcceptDrops(true); 0042 viewport()->setAcceptDrops(true); 0043 0044 // --------------------------------------------------------------- 0045 0046 QWidget* const imagePanel = setupFindSimilarPanel(); 0047 QWidget* const sketchPanel = setupSketchPanel(); 0048 d->findDuplicatesPanel = new FindDuplicatesView(); 0049 0050 d->tabWidget = new QTabWidget(); 0051 d->tabWidget->insertTab(Private::DUPLICATES, d->findDuplicatesPanel, i18nc("Find Duplicates Tab", "Duplicates")); 0052 d->tabWidget->insertTab(Private::SIMILARS, imagePanel, i18nc("Similar Image Tab", "Image")); 0053 d->tabWidget->insertTab(Private::SKETCH, sketchPanel, i18nc("Sketch Search Tab", "Sketch")); 0054 0055 // --------------------------------------------------------------- 0056 0057 d->folderView = new DVBox(); 0058 d->searchTreeView = new EditableSearchTreeView(d->folderView, searchModel, searchModificationHelper); 0059 d->searchTreeView->filteredModel()->listHaarSearches(); 0060 d->searchTreeView->filteredModel()->setListTemporarySearches(true); 0061 d->searchTreeView->setAlbumManagerCurrentAlbum(true); 0062 0063 d->searchFuzzyBar = new SearchTextBarDb(d->folderView, QLatin1String("FuzzySearchViewSearchFuzzyBar")); 0064 d->searchFuzzyBar->setModel(d->searchTreeView->filteredModel(), 0065 AbstractAlbumModel::AlbumIdRole, AbstractAlbumModel::AlbumTitleRole); 0066 d->searchFuzzyBar->setFilterModel(d->searchTreeView->albumFilterModel()); 0067 d->folderView->setContentsMargins(QMargins()); 0068 d->folderView->setSpacing(spacing); 0069 0070 // --------------------------------------------------------------- 0071 0072 QWidget* const mainWidget = new QWidget(this); 0073 QVBoxLayout* const mainLayout = new QVBoxLayout(); 0074 mainLayout->addWidget(d->tabWidget); 0075 mainLayout->addWidget(d->folderView); 0076 mainLayout->setContentsMargins(QMargins()); 0077 mainLayout->setSpacing(0); 0078 mainWidget->setLayout(mainLayout); 0079 0080 setWidget(mainWidget); 0081 0082 // --------------------------------------------------------------- 0083 0084 d->timerSketch = new QTimer(this); 0085 d->timerSketch->setSingleShot(true); 0086 d->timerSketch->setInterval(500); 0087 0088 d->timerImage = new QTimer(this); 0089 d->timerImage->setSingleShot(true); 0090 d->timerImage->setInterval(500); 0091 0092 // --------------------------------------------------------------- 0093 0094 setupConnections(); 0095 0096 // --------------------------------------------------------------- 0097 0098 slotCheckNameEditSketchConditions(); 0099 slotCheckNameEditImageConditions(); 0100 } 0101 0102 FuzzySearchView::~FuzzySearchView() 0103 { 0104 delete d; 0105 } 0106 0107 void FuzzySearchView::setupConnections() 0108 { 0109 connect(d->settings, SIGNAL(setupChanged()), 0110 this, SLOT(slotApplicationSettingsChanged())); 0111 0112 connect(d->tabWidget, SIGNAL(currentChanged(int)), 0113 this, SLOT(slotTabChanged(int))); 0114 0115 connect(d->searchTreeView, SIGNAL(currentAlbumChanged(Album*)), 0116 this, SLOT(slotAlbumSelected(Album*))); 0117 0118 connect(d->hsSelector, SIGNAL(valueChanged(int,int)), 0119 this, SLOT(slotHSChanged(int,int))); 0120 0121 connect(d->vSelector, SIGNAL(valueChanged(int)), 0122 this, SLOT(slotVChanged(int))); 0123 0124 connect(d->penSize, SIGNAL(valueChanged(int)), 0125 d->sketchWidget, SLOT(setPenWidth(int))); 0126 0127 connect(d->resultsSketch, SIGNAL(valueChanged(int)), 0128 this, SLOT(slotDirtySketch())); 0129 0130 connect(d->sketchSearchAlbumSelectors, SIGNAL(signalSelectionChanged()), 0131 this, SLOT(slotDirtySketch())); 0132 0133 connect(d->fuzzySearchAlbumSelectors, SIGNAL(signalSelectionChanged()), 0134 this, SLOT(slotFuzzyAlbumsChanged())); 0135 0136 connect(d->similarityRange, SIGNAL(minChanged(int)), 0137 this, SLOT(slotMinLevelImageChanged(int))); 0138 0139 connect(d->similarityRange, SIGNAL(maxChanged(int)), 0140 this, SLOT(slotMaxLevelImageChanged(int))); 0141 0142 connect(d->resetButton, SIGNAL(clicked()), 0143 this, SLOT(slotClearSketch())); 0144 0145 connect(d->sketchWidget, SIGNAL(signalPenSizeChanged(int)), 0146 d->penSize, SLOT(setValue(int))); 0147 0148 connect(d->sketchWidget, SIGNAL(signalPenColorChanged(QColor)), 0149 this, SLOT(slotPenColorChanged(QColor))); 0150 0151 connect(d->sketchWidget, SIGNAL(signalSketchChanged(QImage)), 0152 this, SLOT(slotDirtySketch())); 0153 0154 connect(d->sketchWidget, SIGNAL(signalUndoRedoStateChanged(bool,bool)), 0155 this, SLOT(slotUndoRedoStateChanged(bool,bool))); 0156 0157 connect(d->undoBtnSketch, SIGNAL(clicked()), 0158 d->sketchWidget, SLOT(slotUndo())); 0159 0160 connect(d->redoBtnSketch, SIGNAL(clicked()), 0161 d->sketchWidget, SLOT(slotRedo())); 0162 0163 connect(d->saveBtnSketch, SIGNAL(clicked()), 0164 this, SLOT(slotSaveSketchSAlbum())); 0165 0166 connect(d->saveBtnImage, SIGNAL(clicked()), 0167 this, SLOT(slotSaveImageSAlbum())); 0168 0169 connect(d->nameEditSketch, SIGNAL(textChanged()), 0170 this, SLOT(slotCheckNameEditSketchConditions())); 0171 0172 connect(d->nameEditSketch, SIGNAL(returnPressed()), 0173 d->saveBtnSketch, SLOT(animateClick())); 0174 0175 connect(d->nameEditImage, SIGNAL(textChanged()), 0176 this, SLOT(slotCheckNameEditImageConditions())); 0177 0178 connect(d->nameEditImage, SIGNAL(returnPressed()), 0179 d->saveBtnImage, SLOT(animateClick())); 0180 0181 connect(d->thumbLoadThread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QPixmap)), 0182 this, SLOT(slotThumbnailLoaded(LoadingDescription,QPixmap))); 0183 0184 connect(d->timerSketch, SIGNAL(timeout()), 0185 this, SLOT(slotTimerSketchDone())); 0186 0187 connect(d->timerImage, SIGNAL(timeout()), 0188 this, SLOT(slotTimerImageDone())); 0189 } 0190 0191 SAlbum* FuzzySearchView::currentAlbum() const 0192 { 0193 return d->searchTreeView->currentAlbum(); 0194 } 0195 0196 void FuzzySearchView::setCurrentAlbum(SAlbum* const album) 0197 { 0198 d->searchTreeView->setCurrentAlbums(QList<Album*>() << album); 0199 } 0200 0201 void FuzzySearchView::newDuplicatesSearch(const QList<PAlbum*>& albums) 0202 { 0203 if (!albums.isEmpty()) 0204 { 0205 d->findDuplicatesPanel->slotSetSelectedAlbums(albums); 0206 } 0207 0208 d->tabWidget->setCurrentIndex(Private::DUPLICATES); 0209 } 0210 0211 void FuzzySearchView::newDuplicatesSearch(const QList<TAlbum*>& albums) 0212 { 0213 if (!albums.isEmpty()) 0214 { 0215 d->findDuplicatesPanel->slotSetSelectedAlbums(albums); 0216 } 0217 0218 d->tabWidget->setCurrentIndex(Private::DUPLICATES); 0219 } 0220 0221 void FuzzySearchView::setConfigGroup(const KConfigGroup& group) 0222 { 0223 StateSavingObject::setConfigGroup(group); 0224 d->searchTreeView->setConfigGroup(group); 0225 } 0226 0227 void FuzzySearchView::doLoadState() 0228 { 0229 KConfigGroup group = getConfigGroup(); 0230 0231 d->tabWidget->setCurrentIndex(group.readEntry(entryName(d->configTabEntry), (int)Private::DUPLICATES)); 0232 d->penSize->setValue(group.readEntry(entryName(d->configPenSketchSizeEntry), 10)); 0233 d->resultsSketch->setValue(group.readEntry(entryName(d->configResultSketchItemsEntry), 10)); 0234 d->hsSelector->setHue(group.readEntry(entryName(d->configPenSketchHueEntry), 180)); 0235 d->hsSelector->setSaturation(group.readEntry(entryName(d->configPenSketchSaturationEntry), 128)); 0236 d->vSelector->setValue(group.readEntry(entryName(d->configPenSkethValueEntry), 255)); 0237 d->similarityRange->setInterval(group.readEntry(entryName(d->configSimilarsThresholdEntry), 90), 0238 group.readEntry(entryName(d->configSimilarsMaxThresholdEntry), 100)); 0239 d->hsSelector->updateContents(); 0240 0241 QColor col; 0242 col.setHsv(d->hsSelector->hue(), 0243 d->hsSelector->saturation(), 0244 d->vSelector->value()); 0245 setColor(col); 0246 0247 d->sketchWidget->setPenWidth(d->penSize->value()); 0248 0249 d->searchTreeView->loadState(); 0250 d->fuzzySearchAlbumSelectors->loadState(); 0251 d->sketchSearchAlbumSelectors->loadState(); 0252 } 0253 0254 void FuzzySearchView::doSaveState() 0255 { 0256 KConfigGroup group = getConfigGroup(); 0257 group.writeEntry(entryName(d->configTabEntry), d->tabWidget->currentIndex()); 0258 group.writeEntry(entryName(d->configPenSketchSizeEntry), d->penSize->value()); 0259 group.writeEntry(entryName(d->configResultSketchItemsEntry), d->resultsSketch->value()); 0260 group.writeEntry(entryName(d->configPenSketchHueEntry), d->hsSelector->hue()); 0261 group.writeEntry(entryName(d->configPenSketchSaturationEntry), d->hsSelector->saturation()); 0262 group.writeEntry(entryName(d->configPenSkethValueEntry), d->vSelector->value()); 0263 group.writeEntry(entryName(d->configSimilarsThresholdEntry), d->similarityRange->minValue()); 0264 group.writeEntry(entryName(d->configSimilarsMaxThresholdEntry), d->similarityRange->maxValue()); 0265 d->searchTreeView->saveState(); 0266 group.sync(); 0267 d->fuzzySearchAlbumSelectors->saveState(); 0268 d->sketchSearchAlbumSelectors->saveState(); 0269 } 0270 0271 void FuzzySearchView::setActive(bool val) 0272 { 0273 d->active = val; 0274 0275 // at first occasion, warn if no fingerprints are available 0276 0277 if (val && !d->fingerprintsChecked && isVisible()) 0278 { 0279 if (!SimilarityDbAccess().db()->hasFingerprints()) 0280 { 0281 QString msg = i18n("Image fingerprints have not yet been generated for your collection. " 0282 "The Similarity Search Tools will not be operational " 0283 "without pre-generated fingerprints. Please generate " 0284 "the fingerprints first."); 0285 Q_EMIT signalNotificationError(msg, DNotificationWidget::Warning); 0286 } 0287 0288 d->fingerprintsChecked = true; 0289 } 0290 0291 int tab = d->tabWidget->currentIndex(); 0292 0293 if (val) 0294 { 0295 slotTabChanged(tab); 0296 } 0297 } 0298 0299 void FuzzySearchView::slotTabChanged(int tab) 0300 { 0301 /** 0302 * Set a list with only one element, albummanager can set only multiple albums 0303 */ 0304 QList<Album*> albums; 0305 0306 switch (tab) 0307 { 0308 case Private::SIMILARS: 0309 { 0310 albums << d->imageSAlbum; 0311 AlbumManager::instance()->setCurrentAlbums(albums); 0312 d->folderView->setVisible(true); 0313 break; 0314 } 0315 0316 case Private::SKETCH: 0317 { 0318 albums << d->sketchSAlbum; 0319 AlbumManager::instance()->setCurrentAlbums(albums); 0320 d->folderView->setVisible(true); 0321 break; 0322 } 0323 0324 default: // DUPLICATES 0325 { 0326 QList<SAlbum*> sAlbums = d->findDuplicatesPanel->currentFindDuplicatesAlbums(); 0327 0328 Q_FOREACH (SAlbum* const album, sAlbums) 0329 { 0330 albums << album; 0331 } 0332 0333 AlbumManager::instance()->setCurrentAlbums(albums); 0334 d->findDuplicatesPanel->populateTreeView(); 0335 d->findDuplicatesPanel->setActive(true); 0336 d->folderView->setVisible(false); 0337 break; 0338 } 0339 } 0340 } 0341 0342 void FuzzySearchView::slotAlbumSelected(Album* album) 0343 { 0344 qCDebug(DIGIKAM_GENERAL_LOG) << "Selected new album" << album; 0345 0346 SAlbum* const salbum = dynamic_cast<SAlbum*>(album); 0347 0348 if (!salbum || !salbum->isHaarSearch()) 0349 { 0350 qCDebug(DIGIKAM_GENERAL_LOG) << "Not a haar search, returning"; 0351 return; 0352 } 0353 0354 if (!d->active) 0355 { 0356 qCDebug(DIGIKAM_GENERAL_LOG) << "Not active, returning"; 0357 return; 0358 } 0359 0360 SearchXmlReader reader(salbum->query()); 0361 reader.readToFirstField(); 0362 QStringView type = reader.attributes().value(QLatin1String("type")); 0363 /* 0364 TODO: apply this settings if necessary. 0365 0366 QStringView numResultsString = reader.attributes().value(QLatin1String("numberofresults")); 0367 QStringView thresholdString = reader.attributes().value(QLatin1String("threshold")); 0368 QStringView maxThresholdString = reader.attributes().value(QLatin1String("maxthreshold")); 0369 QStringView sketchTypeString = reader.attributes().value(QLatin1String("sketchtype")); 0370 */ 0371 if (type == QLatin1String("imageid")) 0372 { 0373 setCurrentImage(reader.valueToLongLong()); 0374 d->imageSAlbum = salbum; 0375 d->tabWidget->setCurrentIndex((int)Private::SIMILARS); 0376 } 0377 else if (type == QLatin1String("signature")) 0378 { 0379 d->sketchSAlbum = salbum; 0380 d->tabWidget->setCurrentIndex((int)Private::SKETCH); 0381 0382 if (reader.readToStartOfElement(QLatin1String("SketchImage"))) 0383 { 0384 d->sketchWidget->setSketchImageFromXML(reader); 0385 } 0386 } 0387 } 0388 0389 void FuzzySearchView::slotApplicationSettingsChanged() 0390 { 0391 d->similarityRange->setRange(d->settings->getMinimumSimilarityBound(), 100); 0392 } 0393 0394 } // namespace Digikam 0395 0396 #include "moc_fuzzysearchview.cpp"