diff --git a/core/app/views/sidebar/peoplesidebarwidget.cpp b/core/app/views/sidebar/peoplesidebarwidget.cpp index f751100d7f..424fc7d0e8 100644 --- a/core/app/views/sidebar/peoplesidebarwidget.cpp +++ b/core/app/views/sidebar/peoplesidebarwidget.cpp @@ -1,221 +1,220 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-12-05 * Description : Side Bar Widget for People * * Copyright (C) 2009-2010 by Johannes Wienke * Copyright (C) 2010-2020 by Gilles Caulier * Copyright (C) 2012 by Andi Clemens * Copyright (C) 2014 by Mohamed_Anwer * Copyright (C) 2010 by Aditya Bhatt * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "peoplesidebarwidget.h" // Qt includes #include #include #include #include #include #include #include #include // KDE includes #include #include // Local includes #include "digikam_debug.h" #include "searchtextbar.h" #include "tagfolderview.h" #include "timelinewidget.h" #include "facescanwidget.h" #include "facesdetector.h" #include "dnotificationwidget.h" namespace Digikam { class Q_DECL_HIDDEN PeopleSideBarWidget::Private { public: explicit Private() : rescanButton(nullptr), searchModificationHelper(nullptr), settingsWdg(nullptr), tagFolderView(nullptr), tagSearchBar(nullptr) { } QPushButton* rescanButton; SearchModificationHelper* searchModificationHelper; FaceScanWidget* settingsWdg; TagFolderView* tagFolderView; SearchTextBar* tagSearchBar; }; PeopleSideBarWidget::PeopleSideBarWidget(QWidget* const parent, TagModel* const model, SearchModificationHelper* const searchModificationHelper) : SidebarWidget(parent), d(new Private) { setObjectName(QLatin1String("People Sidebar")); setProperty("Shortcut", Qt::CTRL + Qt::SHIFT + Qt::Key_F9); d->searchModificationHelper = searchModificationHelper; const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); QWidget* const mainView = new QWidget(this); QScrollArea* const scrollArea = new QScrollArea(this); QVBoxLayout* const mainLayout = new QVBoxLayout(this); mainLayout->addWidget(scrollArea); mainLayout->setContentsMargins(0, 0, spacing, 0); scrollArea->setWidget(mainView); scrollArea->setWidgetResizable(true); QVBoxLayout* const vlay = new QVBoxLayout; d->tagFolderView = new TagFolderView(this, model); d->tagFolderView->setConfigGroup(getConfigGroup()); d->tagFolderView->setExpandNewCurrentItem(true); d->tagFolderView->setAlbumManagerCurrentAlbum(true); d->tagFolderView->setShowDeleteFaceTagsAction(true); d->tagFolderView->filteredModel()->listOnlyTagsWithProperty(TagPropertyName::person()); d->tagFolderView->filteredModel()->setFilterBehavior(AlbumFilterModel::StrictFiltering); d->tagSearchBar = new SearchTextBar(this, QLatin1String("ItemIconViewPeopleSearchBar")); d->tagSearchBar->setHighlightOnResult(true); d->tagSearchBar->setModel(d->tagFolderView->filteredModel(), AbstractAlbumModel::AlbumIdRole, AbstractAlbumModel::AlbumTitleRole); d->tagSearchBar->setFilterModel(d->tagFolderView->albumFilterModel()); d->settingsWdg = new FaceScanWidget(this); d->rescanButton = new QPushButton; d->rescanButton->setText(i18n("Scan collection for faces")); vlay->addWidget(d->tagFolderView, 10); vlay->addWidget(d->tagSearchBar); vlay->addWidget(d->settingsWdg); vlay->addWidget(d->rescanButton); vlay->setContentsMargins(spacing, spacing, spacing, spacing); mainView->setLayout(vlay); connect(d->tagFolderView, SIGNAL(signalFindDuplicates(QList)), this, SIGNAL(signalFindDuplicates(QList))); connect(d->rescanButton, SIGNAL(pressed()), this, SLOT(slotScanForFaces()) ); } PeopleSideBarWidget::~PeopleSideBarWidget() { delete d; } void PeopleSideBarWidget::slotInit() { loadState(); } void PeopleSideBarWidget::setActive(bool active) { emit requestFaceMode(active); if (active) { d->tagFolderView->setCurrentAlbums(QList() << d->tagFolderView->currentAlbum()); } } void PeopleSideBarWidget::doLoadState() { d->tagFolderView->loadState(); d->settingsWdg->loadState(); } void PeopleSideBarWidget::doSaveState() { d->tagFolderView->saveState(); d->settingsWdg->saveState(); } void PeopleSideBarWidget::applySettings() { } void PeopleSideBarWidget::changeAlbumFromHistory(const QList& album) { d->tagFolderView->setCurrentAlbums(album); } void PeopleSideBarWidget::slotScanForFaces() { FaceScanSettings faceScanSettings = d->settingsWdg->settings(); if (!d->settingsWdg->settingsConflicted()) { FacesDetector* const tool = new FacesDetector(faceScanSettings); tool->start(); d->settingsWdg->setEnabled(false); d->rescanButton->setEnabled(false); connect(tool, SIGNAL(signalComplete()), this, SLOT(slotScanComplete())); connect(tool, SIGNAL(signalCanceled()), this, SLOT(slotScanComplete())); } else { emit signalNofificationError(i18n("Face recognition is aborted, because " "there are no identities to recognize. " "Please add new identities."), DNotificationWidget::Information); } } void PeopleSideBarWidget::slotScanComplete() { d->settingsWdg->setEnabled(true); d->rescanButton->setEnabled(true); - d->settingsWdg->resetRetrainAllButton(); } const QIcon PeopleSideBarWidget::getIcon() { return QIcon::fromTheme(QLatin1String("edit-image-face-show")); } const QString PeopleSideBarWidget::getCaption() { return i18nc("Browse images sorted by depicted people", "People"); } } // namespace Digikam diff --git a/core/utilities/facemanagement/widgets/facescanwidget.cpp b/core/utilities/facemanagement/widgets/facescanwidget.cpp index 05804d0b22..8fd7c15cbb 100644 --- a/core/utilities/facemanagement/widgets/facescanwidget.cpp +++ b/core/utilities/facemanagement/widgets/facescanwidget.cpp @@ -1,376 +1,349 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-10-09 * Description : Widget to choose options for face scanning * * Copyright (C) 2010-2012 by Marcel Wiesweg * Copyright (C) 2012-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ // NOTE: Uncomment this line to enable detect and recognize option // Currently this option is hiden, since it's not handled properly and provides confusing functionality => Fix it later //#define ENABLE_DETECT_AND_RECOGNIZE #include "facescanwidget_p.h" namespace Digikam { FaceScanWidget::FaceScanWidget(QWidget* const parent) : QTabWidget(parent), StateSavingObject(this), d(new Private) { setObjectName(d->configName); setupUi(); setupConnections(); } FaceScanWidget::~FaceScanWidget() { delete d; } void FaceScanWidget::doLoadState() { qCDebug(DIGIKAM_GENERAL_LOG) << getConfigGroup().name(); KConfigGroup group = getConfigGroup(); QString mainTask = group.readEntry(entryName(d->configMainTask), d->configValueDetect); if (mainTask == d->configValueRecognizedMarkedFaces) { d->reRecognizeButton->setChecked(true); } else if (mainTask == d->configValueDetectAndRecognize) { #ifdef ENABLE_DETECT_AND_RECOGNIZE d->detectAndRecognizeButton->setChecked(true); #endif d->detectButton->setChecked(true); } else { d->detectButton->setChecked(true); } FaceScanSettings::AlreadyScannedHandling handling; QString skipHandling = group.readEntry(entryName(d->configAlreadyScannedHandling), QString::fromLatin1("Skip")); if (skipHandling == QLatin1String("Rescan")) { handling = FaceScanSettings::Rescan; } else if (skipHandling == QLatin1String("Merge")) { handling = FaceScanSettings::Merge; } else // Skip { handling = FaceScanSettings::Skip; } d->alreadyScannedBox->setCurrentIndex(d->alreadyScannedBox->findData(handling)); d->accuracyInput->setValue(ApplicationSettings::instance()->getFaceDetectionAccuracy() * 100); d->albumSelectors->loadState(); d->useFullCpuButton->setChecked(group.readEntry(entryName(d->configUseFullCpu), false)); - - // do not load retrainAllButton state from config, dangerous } void FaceScanWidget::doSaveState() { qCDebug(DIGIKAM_GENERAL_LOG) << getConfigGroup().name(); KConfigGroup group = getConfigGroup(); QString mainTask; if (d->detectButton->isChecked()) { mainTask = d->configValueDetect; } #ifdef ENABLE_DETECT_AND_RECOGNIZE else if (d->detectAndRecognizeButton->isChecked()) { mainTask = d->configValueDetectAndRecognize; } #endif else // d->reRecognizeButton { mainTask = d->configValueRecognizedMarkedFaces; } group.writeEntry(entryName(d->configMainTask), mainTask); QString handling; switch ((FaceScanSettings::AlreadyScannedHandling)(d->alreadyScannedBox->itemData(d->alreadyScannedBox->currentIndex()).toInt())) { case FaceScanSettings::Skip: handling = QLatin1String("Skip"); break; case FaceScanSettings::Rescan: handling = QLatin1String("Rescan"); break; case FaceScanSettings::Merge: handling = QLatin1String("Merge"); break; } group.writeEntry(entryName(d->configAlreadyScannedHandling), handling); ApplicationSettings::instance()->setFaceDetectionAccuracy(double(d->accuracyInput->value()) / 100); d->albumSelectors->saveState(); group.writeEntry(entryName(d->configUseFullCpu), d->useFullCpuButton->isChecked()); } void FaceScanWidget::setupUi() { // ---- Workflow tab -------- d->workflowWidget = new QWidget(this); d->workflowWidget->setToolTip(i18nc("@tooltip", "digiKam can search for faces in your photos.\n" "When you have identified your friends on a number of photos,\n" "it can also recognize the people shown on your photos.")); QVBoxLayout* const optionLayout = new QVBoxLayout; d->alreadyScannedBox = new SqueezedComboBox; d->alreadyScannedBox->addSqueezedItem(i18nc("@label:listbox", "Skip images already scanned"), FaceScanSettings::Skip); d->alreadyScannedBox->addSqueezedItem(i18nc("@label:listbox", "Scan again and merge results"), FaceScanSettings::Merge); d->alreadyScannedBox->addSqueezedItem(i18nc("@label:listbox", "Clear unconfirmed results and rescan"), FaceScanSettings::Rescan); d->alreadyScannedBox->setCurrentIndex(FaceScanSettings::Skip); d->detectButton = new QRadioButton(i18nc("@option:radio", "Detect faces")); d->detectButton->setToolTip(i18nc("@info", "Find all faces in your photos")); #ifdef ENABLE_DETECT_AND_RECOGNIZE d->detectAndRecognizeButton = new QRadioButton(i18nc("@option:radio", "Detect and recognize faces")); d->detectAndRecognizeButton->setToolTip(i18nc("@info", "Find all faces in your photos and try to recognize which person is depicted")); #endif d->reRecognizeButton = new QRadioButton(i18nc("@option:radio", "Recognize faces")); d->reRecognizeButton->setToolTip(i18nc("@info", "Try again to recognize the people depicted on marked but yet unconfirmed faces.")); optionLayout->addWidget(d->alreadyScannedBox); optionLayout->addWidget(d->detectButton); #ifdef ENABLE_DETECT_AND_RECOGNIZE optionLayout->addWidget(d->detectAndRecognizeButton); #endif optionLayout->addWidget(d->reRecognizeButton); #ifdef ENABLE_DETECT_AND_RECOGNIZE QStyleOptionButton buttonOption; buttonOption.initFrom(d->detectAndRecognizeButton); int indent = style()->subElementRect(QStyle::SE_RadioButtonIndicator, &buttonOption, d->detectAndRecognizeButton).width(); optionLayout->setColumnMinimumWidth(0, indent); #endif d->workflowWidget->setLayout(optionLayout); addTab(d->workflowWidget, i18nc("@title:tab", "Workflow")); // ---- Album tab --------- d->albumSelectors = new AlbumSelectors(QString(), d->configName, this); addTab(d->albumSelectors, i18nc("@title:tab", "Search in")); // ---- Settings tab ------ QWidget* const settingsTab = new QWidget(this); QVBoxLayout* const settingsLayout = new QVBoxLayout(settingsTab); QGroupBox* const accuracyBox = new QGroupBox(i18nc("@groupbox", "Face Accuracy"), settingsTab); QGridLayout* const accuracyGrid = new QGridLayout(accuracyBox); QLabel* const sensitivityLabel = new QLabel(i18nc("@label left extremities of a scale", "Sensitivity"), settingsTab); sensitivityLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); QLabel* const specificityLabel = new QLabel(i18nc("@label right extremities of a scale", "Specificity"), settingsTab); specificityLabel->setAlignment(Qt::AlignTop | Qt::AlignRight); d->accuracyInput = new DIntNumInput(settingsTab); d->accuracyInput->setDefaultValue(70); d->accuracyInput->setRange(0, 100, 10); d->accuracyInput->setToolTip(i18nc("@info:tooltip", "Adjust sensitivity versus specificity: the higher the value, the more accurately faces will " "be recognized, but less faces will be recognized " "(only faces that are very similar to pre-tagged faces are recognized).")); accuracyGrid->addWidget(d->accuracyInput, 0, 0, 1, 3); accuracyGrid->addWidget(sensitivityLabel, 1, 0, 1, 1); accuracyGrid->addWidget(specificityLabel, 1, 2, 1, 1); accuracyGrid->setColumnStretch(1, 10); d->useFullCpuButton = new QCheckBox(settingsTab); d->useFullCpuButton->setText(i18nc("@option:check", "Work on all processor cores")); d->useFullCpuButton->setToolTip(i18nc("@info:tooltip", "Face detection and recognition are time-consuming tasks. " "You can choose if you wish to employ all processor cores " "on your system, or work in the background only on one core.")); - d->retrainAllButton = new QCheckBox(settingsTab); - d->retrainAllButton->setText(i18nc("@option:check", "Clear and rebuild all training data")); - d->retrainAllButton->setToolTip(i18nc("@info:tooltip", - "This will clear all training data for recognition " - "and rebuild it from all available faces.")); - settingsLayout->addWidget(accuracyBox); settingsLayout->addWidget(d->useFullCpuButton); - settingsLayout->addWidget(d->retrainAllButton); settingsLayout->addStretch(10); addTab(settingsTab, i18nc("@title:tab", "Settings")); } void FaceScanWidget::setupConnections() { /* connect(d->detectButton, SIGNAL(toggled(bool)), d->alreadyScannedBox, SLOT(setEnabled(bool))); */ #ifdef ENABLE_DETECT_AND_RECOGNIZE connect(d->detectAndRecognizeButton, SIGNAL(toggled(bool)), d->alreadyScannedBox, SLOT(setEnabled(bool))); #endif connect(d->detectButton, SIGNAL(toggled(bool)), this, SLOT(slotPrepareForDetect(bool))); connect(d->reRecognizeButton, SIGNAL(toggled(bool)), this, SLOT(slotPrepareForRecognize(bool))); - - connect(d->retrainAllButton, SIGNAL(toggled(bool)), - this, SLOT(retrainAllButtonToggled(bool))); } void FaceScanWidget::slotPrepareForDetect(bool status) { d->alreadyScannedBox->setEnabled(status); // Set default for Tag tab as all unchecked d->albumSelectors->resetTAlbumSelection(); } void FaceScanWidget::slotPrepareForRecognize(bool /*status*/) { /** * TODO: * reRecognizeButton always disables d->alreadyScannedBox, while it should be * Find out why and fix it. */ d->alreadyScannedBox->setEnabled(false); // First we set all tags unchecked d->albumSelectors->resetTAlbumSelection(); // Set default for Tag tab so that People and its children are checked AlbumList tagAlbums = AlbumManager::instance()->allTAlbums(); QString people = i18nc("People on your photos", "People"); for (int i = 0 ; i < tagAlbums.size() ; ++i) { Album* const album = tagAlbums[i]; if (album->title() == people || ((album->parent() != nullptr) && (album->parent()->title() == people))) { d->albumSelectors->setTagSelected(album, false); } } } -void FaceScanWidget::retrainAllButtonToggled(bool on) -{ - d->workflowWidget->setEnabled(!on); - d->albumSelectors->setEnabled(!on); -} - bool FaceScanWidget::settingsConflicted() const { return d->settingsConflicted; } FaceScanSettings FaceScanWidget::settings() const { FaceScanSettings settings; d->settingsConflicted = false; - if (d->retrainAllButton->isChecked()) - { - settings.task = FaceScanSettings::RetrainAll; - } - else if (d->detectButton->isChecked()) + if (d->detectButton->isChecked()) { settings.task = FaceScanSettings::Detect; } else { #ifdef ENABLE_DETECT_AND_RECOGNIZE if (d->detectAndRecognizeButton->isChecked()) { settings.task = FaceScanSettings::DetectAndRecognize; } else // recognize only #endif { settings.task = FaceScanSettings::RecognizeMarkedFaces; // preset settingsConflicted as True, since by default there are no tags to recognize d->settingsConflicted = true; } } settings.alreadyScannedHandling = (FaceScanSettings::AlreadyScannedHandling) d->alreadyScannedBox->itemData(d->alreadyScannedBox->currentIndex()).toInt(); settings.accuracy = double(d->accuracyInput->value()) / 100; // TODO: why does the original code append but not assign here??? // settings.albums << d->albumSelectors->selectedAlbumsAndTags(); settings.albums = d->albumSelectors->selectedAlbumsAndTags(); if (d->settingsConflicted) { int numberOfIdentities = FaceDbAccess().db()->getNumberOfIdentities(); d->settingsConflicted = (numberOfIdentities == 0); } settings.useFullCpu = d->useFullCpuButton->isChecked(); return settings; } -void FaceScanWidget::resetRetrainAllButton() -{ - d->retrainAllButton->setChecked(false); -} - } // namespace Digikam diff --git a/core/utilities/facemanagement/widgets/facescanwidget.h b/core/utilities/facemanagement/widgets/facescanwidget.h index dd731868d3..0cdf4df25b 100644 --- a/core/utilities/facemanagement/widgets/facescanwidget.h +++ b/core/utilities/facemanagement/widgets/facescanwidget.h @@ -1,82 +1,76 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-10-09 * Description : Widget to choose options for face scanning * * Copyright (C) 2010-2012 by Marcel Wiesweg * Copyright (C) 2012-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_FACE_SCAN_WIDGET_H #define DIGIKAM_FACE_SCAN_WIDGET_H // Qt includes #include // Local includes #include "statesavingobject.h" #include "facescansettings.h" namespace Digikam { class FaceScanWidget : public QTabWidget, public StateSavingObject { Q_OBJECT public: explicit FaceScanWidget(QWidget* const parent = nullptr); ~FaceScanWidget(); bool settingsConflicted() const; FaceScanSettings settings() const; - void resetRetrainAllButton(); - protected: void doLoadState(); void doSaveState(); -protected Q_SLOTS: - - void retrainAllButtonToggled(bool on); - private: void setupUi(); void setupConnections(); private Q_SLOTS: void slotPrepareForDetect(bool status); void slotPrepareForRecognize(bool status); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_FACE_SCAN_WIDGET_H diff --git a/core/utilities/facemanagement/widgets/facescanwidget_p.h b/core/utilities/facemanagement/widgets/facescanwidget_p.h index 9dbc586f5e..c39ab3595f 100644 --- a/core/utilities/facemanagement/widgets/facescanwidget_p.h +++ b/core/utilities/facemanagement/widgets/facescanwidget_p.h @@ -1,125 +1,123 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-10-09 * Description : Widget to choose options for face scanning * * Copyright (C) 2010-2012 by Marcel Wiesweg * Copyright (C) 2012-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_FACE_SCAN_WIDGET_P_H #define DIGIKAM_FACE_SCAN_WIDGET_P_H #include "facescanwidget.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include // Local includes #include "digikam_config.h" #include "dlayoutbox.h" #include "dnuminput.h" #include "digikam_debug.h" #include "albummodel.h" #include "albumselectors.h" #include "albummanager.h" #include "applicationsettings.h" #include "squeezedcombobox.h" #include "dexpanderbox.h" #include "facedbaccess.h" #include "facedb.h" namespace Digikam { class Q_DECL_HIDDEN FaceScanWidget::Private { public: explicit Private() : workflowWidget(nullptr), detectAndRecognizeButton(nullptr), detectButton(nullptr), alreadyScannedBox(nullptr), reRecognizeButton(nullptr), tabWidget(nullptr), albumSelectors(nullptr), accuracyInput(nullptr), useFullCpuButton(nullptr), - retrainAllButton(nullptr), configName(QLatin1String("Face Detection Dialog")), configMainTask(QLatin1String("Face Scan Main Task")), configValueDetect(QLatin1String("Detect")), configValueDetectAndRecognize(QLatin1String("Detect and Recognize Faces")), configValueRecognizedMarkedFaces(QLatin1String("Recognize Marked Faces")), configAlreadyScannedHandling(QLatin1String("Already Scanned Handling")), configUseFullCpu(QLatin1String("Use Full CPU")), settingsConflicted(false) { } QWidget* workflowWidget; QRadioButton* detectAndRecognizeButton; QRadioButton* detectButton; SqueezedComboBox* alreadyScannedBox; QRadioButton* reRecognizeButton; QTabWidget* tabWidget; AlbumSelectors* albumSelectors; DIntNumInput* accuracyInput; QCheckBox* useFullCpuButton; - QCheckBox* retrainAllButton; const QString configName; const QString configMainTask; const QString configValueDetect; const QString configValueDetectAndRecognize; const QString configValueRecognizedMarkedFaces; const QString configAlreadyScannedHandling; const QString configUseFullCpu; bool settingsConflicted; }; } // namespace Digikam #endif // DIGIKAM_FACE_SCAN_WIDGET_P_H diff --git a/core/utilities/maintenance/maintenancedlg.cpp b/core/utilities/maintenance/maintenancedlg.cpp index 194c2015f8..54603fd6f6 100644 --- a/core/utilities/maintenance/maintenancedlg.cpp +++ b/core/utilities/maintenance/maintenancedlg.cpp @@ -1,580 +1,597 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-01-30 * Description : maintenance dialog * * Copyright (C) 2012-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "maintenancedlg.h" // Qt includes #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include #include // Local includes #include "dlayoutbox.h" #include "dexpanderbox.h" #include "dnuminput.h" #include "digikam_config.h" #include "setup.h" #include "albumselectors.h" #include "facescansettings.h" #include "imagequalitycontainer.h" #include "metadatasynchronizer.h" #include "dxmlguiwindow.h" #include "applicationsettings.h" #include "drangebox.h" namespace Digikam { class Q_DECL_HIDDEN MaintenanceDlg::Private { public: enum Operation { Options = 0, NewItems, DbCleanup, Thumbnails, FingerPrints, Duplicates, FaceManagement, ImageQualitySorter, MetadataSync, Stretch }; public: explicit Private() : buttons(nullptr), logo(nullptr), title(nullptr), scanThumbs(nullptr), scanFingerPrints(nullptr), useMutiCoreCPU(nullptr), cleanThumbsDb(nullptr), cleanFacesDb(nullptr), + retrainAllFaces(nullptr), shrinkDatabases(nullptr), qualityScanMode(nullptr), metadataSetup(nullptr), qualitySetup(nullptr), syncDirection(nullptr), similarityRangeBox(nullptr), dupeRestrictionBox(nullptr), vbox(nullptr), vbox2(nullptr), vbox3(nullptr), + vbox4(nullptr), duplicatesBox(nullptr), - hbox3(nullptr), similarityRange(nullptr), faceScannedHandling(nullptr), searchResultRestriction(nullptr), expanderBox(nullptr), albumSelectors(nullptr) { } static const QString configGroupName; static const QString configUseMutiCoreCPU; static const QString configNewItems; static const QString configThumbnails; static const QString configScanThumbs; static const QString configFingerPrints; static const QString configScanFingerPrints; static const QString configDuplicates; static const QString configMinSimilarity; static const QString configMaxSimilarity; static const QString configDuplicatesRestriction; static const QString configFaceManagement; static const QString configFaceScannedHandling; static const QString configImageQualitySorter; static const QString configQualityScanMode; static const QString configMetadataSync; static const QString configCleanupDatabase; static const QString configCleanupThumbDatabase; static const QString configCleanupFacesDatabase; static const QString configShrinkDatabases; static const QString configSyncDirection; QDialogButtonBox* buttons; QLabel* logo; QLabel* title; QCheckBox* scanThumbs; QCheckBox* scanFingerPrints; QCheckBox* useMutiCoreCPU; QCheckBox* cleanThumbsDb; QCheckBox* cleanFacesDb; + QCheckBox* retrainAllFaces; QCheckBox* shrinkDatabases; QComboBox* qualityScanMode; QPushButton* metadataSetup; QPushButton* qualitySetup; QComboBox* syncDirection; DHBox* similarityRangeBox; DHBox* dupeRestrictionBox; DVBox* vbox; DVBox* vbox2; DVBox* vbox3; + DVBox* vbox4; DVBox* duplicatesBox; - DHBox* hbox3; DIntRangeBox* similarityRange; QComboBox* faceScannedHandling; QComboBox* searchResultRestriction; DExpanderBox* expanderBox; AlbumSelectors* albumSelectors; }; const QString MaintenanceDlg::Private::configGroupName(QLatin1String("MaintenanceDlg Settings")); const QString MaintenanceDlg::Private::configUseMutiCoreCPU(QLatin1String("UseMutiCoreCPU")); const QString MaintenanceDlg::Private::configNewItems(QLatin1String("NewItems")); const QString MaintenanceDlg::Private::configThumbnails(QLatin1String("Thumbnails")); const QString MaintenanceDlg::Private::configScanThumbs(QLatin1String("ScanThumbs")); const QString MaintenanceDlg::Private::configFingerPrints(QLatin1String("FingerPrints")); const QString MaintenanceDlg::Private::configScanFingerPrints(QLatin1String("ScanFingerPrints")); const QString MaintenanceDlg::Private::configDuplicates(QLatin1String("Duplicates")); const QString MaintenanceDlg::Private::configMinSimilarity(QLatin1String("minSimilarity")); const QString MaintenanceDlg::Private::configMaxSimilarity(QLatin1String("maxSimilarity")); const QString MaintenanceDlg::Private::configDuplicatesRestriction(QLatin1String("duplicatesRestriction")); const QString MaintenanceDlg::Private::configFaceManagement(QLatin1String("FaceManagement")); const QString MaintenanceDlg::Private::configFaceScannedHandling(QLatin1String("FaceScannedHandling")); const QString MaintenanceDlg::Private::configImageQualitySorter(QLatin1String("ImageQualitySorter")); const QString MaintenanceDlg::Private::configQualityScanMode(QLatin1String("QualityScanMode")); const QString MaintenanceDlg::Private::configMetadataSync(QLatin1String("MetadataSync")); const QString MaintenanceDlg::Private::configSyncDirection(QLatin1String("SyncDirection")); const QString MaintenanceDlg::Private::configCleanupDatabase(QLatin1String("CleanupDatabase")); const QString MaintenanceDlg::Private::configCleanupThumbDatabase(QLatin1String("CleanupThumbDatabase")); const QString MaintenanceDlg::Private::configCleanupFacesDatabase(QLatin1String("CleanupFacesDatabase")); const QString MaintenanceDlg::Private::configShrinkDatabases(QLatin1String("ShrinkDatabases")); MaintenanceDlg::MaintenanceDlg(QWidget* const parent) : QDialog(parent), d(new Private) { setWindowFlags((windowFlags() & ~Qt::Dialog) | Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowMinMaxButtonsHint); setWindowTitle(i18n("Maintenance")); setModal(true); d->buttons = new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); d->buttons->button(QDialogButtonBox::Cancel)->setDefault(true); QScrollArea* const main = new QScrollArea(this); QWidget* const page = new QWidget(main->viewport()); main->setWidget(page); main->setWidgetResizable(true); QGridLayout* const grid = new QGridLayout(page); d->logo = new QLabel(page); d->logo->setPixmap(QIcon::fromTheme(QLatin1String("digikam")).pixmap(QSize(48,48))); d->title = new QLabel(i18n("Select Maintenance Operations to Process"), page); d->expanderBox = new DExpanderBox(page); // -------------------------------------------------------------------------------------- DVBox* const options = new DVBox; d->albumSelectors = new AlbumSelectors(i18nc("@label", "Process items from:"), d->configGroupName, options); d->useMutiCoreCPU = new QCheckBox(i18nc("@option:check", "Work on all processor cores (when it possible)"), options); d->expanderBox->insertItem(Private::Options, options, QIcon::fromTheme(QLatin1String("configure")), i18n("Common Options"), QLatin1String("Options"), true); // -------------------------------------------------------------------------------------- d->expanderBox->insertItem(Private::NewItems, new QLabel(i18n("No option
" "Note: only Albums Collection are processed by this tool.
")), QIcon::fromTheme(QLatin1String("view-refresh")), i18n("Scan for new items"), QLatin1String("NewItems"), false); d->expanderBox->setCheckBoxVisible(Private::NewItems, true); // -------------------------------------------------------------------------------------- d->vbox3 = new DVBox; new QLabel(i18n("Note: If activated, the Core DB is always cleaned. You can select additional databases for cleaning.
" " If you select one of the options below, the process may take much time and can freeze digiKam temporarily
" " in order to make sure that no database corruption occurs.
"), d->vbox3); d->cleanThumbsDb = new QCheckBox(i18n("Also clean up the thumbnail database."), d->vbox3); d->cleanFacesDb = new QCheckBox(i18n("Also clean up the faces database."), d->vbox3); d->shrinkDatabases = new QCheckBox(i18n("Also shrink all databases if possible."), d->vbox3); d->shrinkDatabases->setToolTip(i18n("This option leads to the vacuuming (shrinking) of the databases. " "Vacuuming is supported both for SQLite and MySQL.")); d->expanderBox->insertItem(Private::DbCleanup, d->vbox3, QIcon::fromTheme(QLatin1String("run-build")), i18n("Perform database cleaning"), QLatin1String("DbCleanup"), false); d->expanderBox->setCheckBoxVisible(Private::DbCleanup, true); // -------------------------------------------------------------------------------------- d->scanThumbs = new QCheckBox(i18n("Scan for changed or non-cataloged items (faster)")); d->expanderBox->insertItem(Private::Thumbnails, d->scanThumbs, QIcon::fromTheme(QLatin1String("view-process-all")), i18n("Rebuild Thumbnails"), QLatin1String("Thumbnails"), false); d->expanderBox->setCheckBoxVisible(Private::Thumbnails, true); // -------------------------------------------------------------------------------------- d->scanFingerPrints = new QCheckBox(i18n("Scan for changed or non-cataloged items (faster)")); d->expanderBox->insertItem(Private::FingerPrints, d->scanFingerPrints, QIcon::fromTheme(QLatin1String("run-build")), i18n("Rebuild Finger-prints"), QLatin1String("Fingerprints"), false); d->expanderBox->setCheckBoxVisible(Private::FingerPrints, true); // -------------------------------------------------------------------------------------- const ApplicationSettings * settings = ApplicationSettings::instance(); d->duplicatesBox = new DVBox; d->similarityRangeBox = new DHBox(d->duplicatesBox); new QLabel(i18n("Similarity range (in percents): "), d->similarityRangeBox); QWidget* const space = new QWidget(d->similarityRangeBox); d->similarityRangeBox->setStretchFactor(space, 10); d->similarityRange = new DIntRangeBox(d->similarityRangeBox); d->similarityRange->setSuffix(QLatin1String("%")); if (settings) { d->similarityRange->setRange(settings->getMinimumSimilarityBound(), 100); d->similarityRange->setInterval(settings->getDuplicatesSearchLastMinSimilarity(), settings->getDuplicatesSearchLastMaxSimilarity()); } else { d->similarityRange->setRange(40, 100); d->similarityRange->setInterval(90, 100); } d->dupeRestrictionBox = new DHBox(d->duplicatesBox); new QLabel(i18n("Restriction on duplicates:"), d->dupeRestrictionBox); QWidget* const space4 = new QWidget(d->dupeRestrictionBox); d->dupeRestrictionBox->setStretchFactor(space4, 10); d->searchResultRestriction = new QComboBox(d->dupeRestrictionBox); d->searchResultRestriction->addItem(i18n("No restriction"), HaarIface::DuplicatesSearchRestrictions::None); d->searchResultRestriction->addItem(i18n("Restrict to album of reference image"), HaarIface::DuplicatesSearchRestrictions::SameAlbum); d->searchResultRestriction->addItem(i18n("Exclude album of reference image"), HaarIface::DuplicatesSearchRestrictions::DifferentAlbum); // Load the last choice from application settings. HaarIface::DuplicatesSearchRestrictions restrictions = HaarIface::DuplicatesSearchRestrictions::None; if (settings) { restrictions = (HaarIface::DuplicatesSearchRestrictions) settings->getDuplicatesSearchRestrictions(); } d->searchResultRestriction->setCurrentIndex(d->searchResultRestriction->findData(restrictions)); d->expanderBox->insertItem(Private::Duplicates, d->duplicatesBox, QIcon::fromTheme(QLatin1String("tools-wizard")), i18n("Find Duplicate Items"), QLatin1String("Duplicates"), false); d->expanderBox->setCheckBoxVisible(Private::Duplicates, true); // -------------------------------------------------------------------------------------- - d->hbox3 = new DHBox; - new QLabel(i18n("Faces data management: "), d->hbox3); - QWidget* const space3 = new QWidget(d->hbox3); - d->hbox3->setStretchFactor(space3, 10); - d->faceScannedHandling = new QComboBox(d->hbox3); + d->vbox4 = new DVBox; + DHBox* const hbox3 = new DHBox(d->vbox4); + new QLabel(i18n("Faces data management: "), hbox3); + QWidget* const space3 = new QWidget(hbox3); + hbox3->setStretchFactor(space3, 10); + d->faceScannedHandling = new QComboBox(hbox3); d->faceScannedHandling->addItem(i18n("Skip images already scanned"), FaceScanSettings::Skip); d->faceScannedHandling->addItem(i18n("Scan again and merge results"), FaceScanSettings::Merge); d->faceScannedHandling->addItem(i18n("Clear unconfirmed results and rescan"), FaceScanSettings::Rescan); - d->expanderBox->insertItem(Private::FaceManagement, d->hbox3, QIcon::fromTheme(QLatin1String("edit-image-face-detect")), + + d->retrainAllFaces = new QCheckBox(d->vbox4); + d->retrainAllFaces->setText(i18nc("@option:check", "Clear and rebuild all training data")); + d->retrainAllFaces->setToolTip(i18nc("@info:tooltip", + "This will clear all training data for recognition " + "and rebuild it from all available faces.")); + d->expanderBox->insertItem(Private::FaceManagement, d->vbox4, QIcon::fromTheme(QLatin1String("edit-image-face-detect")), i18n("Detect and recognize Faces"), QLatin1String("FaceManagement"), false); d->expanderBox->setCheckBoxVisible(Private::FaceManagement, true); // -------------------------------------------------------------------------------------- d->vbox = new DVBox; DHBox* const hbox11 = new DHBox(d->vbox); new QLabel(i18n("Scan Mode: "), hbox11); QWidget* const space7 = new QWidget(hbox11); hbox11->setStretchFactor(space7, 10); d->qualityScanMode = new QComboBox(hbox11); d->qualityScanMode->addItem(i18n("Clean all and re-scan"), ImageQualitySorter::AllItems); d->qualityScanMode->addItem(i18n("Scan non-assigned only"), ImageQualitySorter::NonAssignedItems); DHBox* const hbox12 = new DHBox(d->vbox); new QLabel(i18n("Check quality sorter setup panel for details: "), hbox12); QWidget* const space2 = new QWidget(hbox12); hbox12->setStretchFactor(space2, 10); d->qualitySetup = new QPushButton(i18n("Settings..."), hbox12); d->expanderBox->insertItem(Private::ImageQualitySorter, d->vbox, QIcon::fromTheme(QLatin1String("flag-green")), i18n("Image Quality Sorter"), QLatin1String("ImageQualitySorter"), false); d->expanderBox->setCheckBoxVisible(Private::ImageQualitySorter, true); // -------------------------------------------------------------------------------------- d->vbox2 = new DVBox; DHBox* const hbox21 = new DHBox(d->vbox2); new QLabel(i18n("Sync Direction: "), hbox21); QWidget* const space5 = new QWidget(hbox21); hbox21->setStretchFactor(space5, 10); d->syncDirection = new QComboBox(hbox21); d->syncDirection->addItem(i18n("From database to image metadata"), MetadataSynchronizer::WriteFromDatabaseToFile); d->syncDirection->addItem(i18n("From image metadata to database"), MetadataSynchronizer::ReadFromFileToDatabase); DHBox* const hbox22 = new DHBox(d->vbox2); new QLabel(i18n("Check metadata setup panel for details: "), hbox22); QWidget* const space6 = new QWidget(hbox22); hbox22->setStretchFactor(space6, 10); d->metadataSetup = new QPushButton(i18n("Settings..."), hbox22); d->expanderBox->insertItem(Private::MetadataSync, d->vbox2, QIcon::fromTheme(QLatin1String("run-build-file")), i18n("Sync Metadata and Database"), QLatin1String("MetadataSync"), false); d->expanderBox->setCheckBoxVisible(Private::MetadataSync, true); d->expanderBox->insertStretch(Private::Stretch); // -------------------------------------------------------------------------------------- grid->addWidget(d->logo, 0, 0, 1, 1); grid->addWidget(d->title, 0, 1, 1, 1); grid->addWidget(d->expanderBox, 5, 0, 3, 2); grid->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); grid->setContentsMargins(QMargins()); grid->setColumnStretch(1, 10); grid->setRowStretch(5, 10); QVBoxLayout* const vbx = new QVBoxLayout(this); vbx->addWidget(main); vbx->addWidget(d->buttons); setLayout(vbx); // -------------------------------------------------------------------------------------- connect(d->buttons->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(slotOk())); connect(d->buttons->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(reject())); connect(d->buttons->button(QDialogButtonBox::Help), SIGNAL(clicked()), this, SLOT(slotHelp())); connect(d->expanderBox, SIGNAL(signalItemToggled(int,bool)), this, SLOT(slotItemToggled(int,bool))); connect(d->metadataSetup, SIGNAL(clicked()), this, SLOT(slotMetadataSetup())); connect(d->qualitySetup, SIGNAL(clicked()), this, SLOT(slotQualitySetup())); + connect(d->retrainAllFaces, &QCheckBox::toggled, + [=](bool on) + { + hbox3->setEnabled(!on); + }); + // -------------------------------------------------------------------------------------- readSettings(); } MaintenanceDlg::~MaintenanceDlg() { delete d; } void MaintenanceDlg::slotOk() { writeSettings(); accept(); } MaintenanceSettings MaintenanceDlg::settings() const { MaintenanceSettings prm; prm.wholeAlbums = d->albumSelectors->wholeAlbumsChecked(); prm.wholeTags = d->albumSelectors->wholeTagsChecked(); prm.albums = d->albumSelectors->selectedAlbums(); prm.tags = d->albumSelectors->selectedTags(); prm.useMutiCoreCPU = d->useMutiCoreCPU->isChecked(); prm.newItems = d->expanderBox->isChecked(Private::NewItems); prm.databaseCleanup = d->expanderBox->isChecked(Private::DbCleanup); prm.cleanThumbDb = d->cleanThumbsDb->isChecked(); prm.cleanFacesDb = d->cleanFacesDb->isChecked(); prm.shrinkDatabases = d->shrinkDatabases->isChecked(); prm.thumbnails = d->expanderBox->isChecked(Private::Thumbnails); prm.scanThumbs = d->scanThumbs->isChecked(); prm.fingerPrints = d->expanderBox->isChecked(Private::FingerPrints); prm.scanFingerPrints = d->scanFingerPrints->isChecked(); prm.duplicates = d->expanderBox->isChecked(Private::Duplicates); prm.minSimilarity = d->similarityRange->minValue(); prm.maxSimilarity = d->similarityRange->maxValue(); prm.duplicatesRestriction = (HaarIface::DuplicatesSearchRestrictions) d->searchResultRestriction->itemData(d->searchResultRestriction->currentIndex()).toInt(); prm.faceManagement = d->expanderBox->isChecked(Private::FaceManagement); prm.faceSettings.alreadyScannedHandling = (FaceScanSettings::AlreadyScannedHandling) d->faceScannedHandling->itemData(d->faceScannedHandling->currentIndex()).toInt(); + prm.faceSettings.task = d->retrainAllFaces->isChecked() ? FaceScanSettings::RetrainAll + : FaceScanSettings::DetectAndRecognize; prm.faceSettings.albums = d->albumSelectors->selectedAlbums(); prm.qualitySort = d->expanderBox->isChecked(Private::ImageQualitySorter); prm.qualityScanMode = d->qualityScanMode->itemData(d->qualityScanMode->currentIndex()).toInt(); ImageQualityContainer imgq; imgq.readFromConfig(); prm.quality = imgq; prm.metadataSync = d->expanderBox->isChecked(Private::MetadataSync); prm.syncDirection = d->syncDirection->itemData(d->syncDirection->currentIndex()).toInt(); return prm; } void MaintenanceDlg::readSettings() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(d->configGroupName); d->expanderBox->readSettings(group); d->albumSelectors->loadState(); MaintenanceSettings prm; d->useMutiCoreCPU->setChecked(group.readEntry(d->configUseMutiCoreCPU, prm.useMutiCoreCPU)); d->expanderBox->setChecked(Private::NewItems, group.readEntry(d->configNewItems, prm.newItems)); d->expanderBox->setChecked(Private::DbCleanup, group.readEntry(d->configCleanupDatabase, prm.databaseCleanup)); d->cleanThumbsDb->setChecked(group.readEntry(d->configCleanupThumbDatabase, prm.cleanThumbDb)); d->cleanFacesDb->setChecked(group.readEntry(d->configCleanupFacesDatabase, prm.cleanFacesDb)); d->shrinkDatabases->setChecked(group.readEntry(d->configShrinkDatabases, prm.shrinkDatabases)); d->expanderBox->setChecked(Private::Thumbnails, group.readEntry(d->configThumbnails, prm.thumbnails)); d->scanThumbs->setChecked(group.readEntry(d->configScanThumbs, prm.scanThumbs)); d->expanderBox->setChecked(Private::FingerPrints, group.readEntry(d->configFingerPrints, prm.fingerPrints)); d->scanFingerPrints->setChecked(group.readEntry(d->configScanFingerPrints, prm.scanFingerPrints)); d->expanderBox->setChecked(Private::Duplicates, group.readEntry(d->configDuplicates, prm.duplicates)); d->similarityRange->setInterval(group.readEntry(d->configMinSimilarity, prm.minSimilarity), group.readEntry(d->configMaxSimilarity, prm.maxSimilarity)); int restrictions = d->searchResultRestriction->findData(group.readEntry(d->configDuplicatesRestriction, (int)prm.duplicatesRestriction)); d->searchResultRestriction->setCurrentIndex(restrictions); d->expanderBox->setChecked(Private::FaceManagement, group.readEntry(d->configFaceManagement, prm.faceManagement)); d->faceScannedHandling->setCurrentIndex(group.readEntry(d->configFaceScannedHandling, (int)prm.faceSettings.alreadyScannedHandling)); d->expanderBox->setChecked(Private::ImageQualitySorter, group.readEntry(d->configImageQualitySorter, prm.qualitySort)); d->qualityScanMode->setCurrentIndex(group.readEntry(d->configQualityScanMode, prm.qualityScanMode)); d->expanderBox->setChecked(Private::MetadataSync, group.readEntry(d->configMetadataSync, prm.metadataSync)); d->syncDirection->setCurrentIndex(group.readEntry(d->configSyncDirection, prm.syncDirection)); for (int i = Private::NewItems ; i < Private::Stretch ; ++i) { slotItemToggled(i, d->expanderBox->isChecked(i)); } winId(); windowHandle()->resize(800, 600); DXmlGuiWindow::restoreWindowSize(windowHandle(), group); resize(windowHandle()->size()); } void MaintenanceDlg::writeSettings() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(d->configGroupName); d->expanderBox->writeSettings(group); d->albumSelectors->saveState(); MaintenanceSettings prm = settings(); group.writeEntry(d->configUseMutiCoreCPU, prm.useMutiCoreCPU); group.writeEntry(d->configNewItems, prm.newItems); group.writeEntry(d->configCleanupDatabase, prm.databaseCleanup); group.writeEntry(d->configCleanupThumbDatabase, prm.cleanThumbDb); group.writeEntry(d->configCleanupFacesDatabase, prm.cleanFacesDb); group.writeEntry(d->configShrinkDatabases, prm.shrinkDatabases); group.writeEntry(d->configThumbnails, prm.thumbnails); group.writeEntry(d->configScanThumbs, prm.scanThumbs); group.writeEntry(d->configFingerPrints, prm.fingerPrints); group.writeEntry(d->configScanFingerPrints, prm.scanFingerPrints); group.writeEntry(d->configDuplicates, prm.duplicates); group.writeEntry(d->configMinSimilarity, prm.minSimilarity); group.writeEntry(d->configMaxSimilarity, prm.maxSimilarity); group.writeEntry(d->configDuplicatesRestriction, (int)prm.duplicatesRestriction); group.writeEntry(d->configFaceManagement, prm.faceManagement); group.writeEntry(d->configFaceScannedHandling, (int)prm.faceSettings.alreadyScannedHandling); group.writeEntry(d->configImageQualitySorter, prm.qualitySort); group.writeEntry(d->configQualityScanMode, prm.qualityScanMode); group.writeEntry(d->configMetadataSync, prm.metadataSync); group.writeEntry(d->configSyncDirection, prm.syncDirection); DXmlGuiWindow::saveWindowSize(windowHandle(), group); } void MaintenanceDlg::slotItemToggled(int index, bool b) { switch (index) { case Private::Thumbnails: d->scanThumbs->setEnabled(b); break; case Private::FingerPrints: d->scanFingerPrints->setEnabled(b); break; case Private::Duplicates: d->duplicatesBox->setEnabled(b); break; case Private::FaceManagement: - d->hbox3->setEnabled(b); + d->vbox4->setEnabled(b); break; case Private::ImageQualitySorter: d->vbox->setEnabled(b); break; case Private::MetadataSync: d->vbox2->setEnabled(b); break; case Private::DbCleanup: d->vbox3->setEnabled(b); break; default : // NewItems break; } } void MaintenanceDlg::slotMetadataSetup() { Setup::execSinglePage(this, Setup::MetadataPage); } void MaintenanceDlg::slotQualitySetup() { Setup::execSinglePage(this, Setup::ImageQualityPage); } void MaintenanceDlg::slotHelp() { DXmlGuiWindow::openHandbook(); } } // namespace Digikam