File indexing completed on 2024-05-12 15:55:38

0001 /* SPDX-FileCopyrightText: 2012-2020 The KPhotoAlbum Development Team
0002 
0003    SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 */
0005 
0006 #include "DuplicateMerger.h"
0007 
0008 #include "DuplicateMatch.h"
0009 #include "MergeToolTip.h"
0010 
0011 #include <DB/ImageDB.h>
0012 #include <DB/ImageInfo.h>
0013 #include <DB/MD5.h>
0014 #include <Utilities/DeleteFiles.h>
0015 #include <Utilities/ShowBusyCursor.h>
0016 #include <kpabase/FileName.h>
0017 #include <kpabase/FileNameList.h>
0018 
0019 #include <KLocalizedString>
0020 #include <QDebug>
0021 #include <QDialogButtonBox>
0022 #include <QLabel>
0023 #include <QPushButton>
0024 #include <QRadioButton>
0025 #include <QScrollArea>
0026 #include <QVBoxLayout>
0027 
0028 namespace MainWindow
0029 {
0030 
0031 DuplicateMerger::DuplicateMerger(QWidget *parent)
0032     : QDialog(parent)
0033 {
0034     setAttribute(Qt::WA_DeleteOnClose);
0035     resize(800, 600);
0036 
0037     QWidget *top = new QWidget(this);
0038     QVBoxLayout *topLayout = new QVBoxLayout(top);
0039     setLayout(topLayout);
0040     topLayout->addWidget(top);
0041 
0042     QString txt = i18n("<p>Below is a list of all images that are duplicate in your database.<br/>"
0043                        "Select which you want merged, and which of the duplicates should be kept.<br/>"
0044                        "The tag and description from the deleted images will be transferred to the kept image</p>");
0045     QLabel *label = new QLabel(txt);
0046     QFont fnt = font();
0047     fnt.setPixelSize(18);
0048     label->setFont(fnt);
0049     topLayout->addWidget(label);
0050 
0051     m_trash = new QRadioButton(i18n("Move to &trash"));
0052     m_deleteFromDisk = new QRadioButton(i18n("&Delete from disk"));
0053     QRadioButton *blockFromDB = new QRadioButton(i18n("&Block from database"));
0054     m_trash->setChecked(true);
0055 
0056     topLayout->addSpacing(10);
0057     topLayout->addWidget(m_trash);
0058     topLayout->addWidget(m_deleteFromDisk);
0059     topLayout->addWidget(blockFromDB);
0060     topLayout->addSpacing(10);
0061 
0062     QScrollArea *scrollArea = new QScrollArea;
0063     topLayout->addWidget(scrollArea);
0064     scrollArea->setWidgetResizable(true);
0065 
0066     m_container = new QWidget(scrollArea);
0067     m_scrollLayout = new QVBoxLayout(m_container);
0068     scrollArea->setWidget(m_container);
0069 
0070     m_selectionCount = new QLabel;
0071     topLayout->addWidget(m_selectionCount);
0072 
0073     QDialogButtonBox *buttonBox = new QDialogButtonBox();
0074 
0075     m_selectAllButton = buttonBox->addButton(i18n("Select &All"), QDialogButtonBox::YesRole);
0076     m_selectNoneButton = buttonBox->addButton(i18n("Select &None"), QDialogButtonBox::NoRole);
0077     m_okButton = buttonBox->addButton(QDialogButtonBox::Ok);
0078     m_cancelButton = buttonBox->addButton(QDialogButtonBox::Cancel);
0079 
0080     connect(m_selectAllButton, &QPushButton::clicked, this, QOverload<>::of(&DuplicateMerger::selectAll));
0081     connect(m_selectNoneButton, &QPushButton::clicked, this, &DuplicateMerger::selectNone);
0082     connect(m_okButton, &QPushButton::clicked, this, &DuplicateMerger::go);
0083     connect(m_cancelButton, &QPushButton::clicked, this, &DuplicateMerger::reject);
0084 
0085     topLayout->addWidget(buttonBox);
0086 
0087     findDuplicates();
0088 }
0089 
0090 MainWindow::DuplicateMerger::~DuplicateMerger()
0091 {
0092     MergeToolTip::destroy();
0093 }
0094 
0095 void DuplicateMerger::selectAll()
0096 {
0097     selectAll(true);
0098 }
0099 
0100 void DuplicateMerger::selectNone()
0101 {
0102     selectAll(false);
0103 }
0104 
0105 void DuplicateMerger::go()
0106 {
0107     Utilities::DeleteMethod method = Utilities::BlockFromDatabase;
0108 
0109     if (m_trash->isChecked()) {
0110         method = Utilities::MoveToTrash;
0111     } else if (m_deleteFromDisk->isChecked()) {
0112         method = Utilities::DeleteFromDisk;
0113     }
0114 
0115     for (DuplicateMatch *selector : qAsConst(m_selectors)) {
0116         selector->execute(method);
0117     }
0118 
0119     accept();
0120 }
0121 
0122 void DuplicateMerger::updateSelectionCount()
0123 {
0124     int total = 0;
0125     int selected = 0;
0126 
0127     for (const DuplicateMatch *selector : qAsConst(m_selectors)) {
0128         ++total;
0129         if (selector->selected())
0130             ++selected;
0131     }
0132 
0133     m_selectionCount->setText(i18n("%1 of %2 selected", selected, total));
0134     m_okButton->setEnabled(selected > 0);
0135 }
0136 
0137 void DuplicateMerger::findDuplicates()
0138 {
0139     Utilities::ShowBusyCursor dummy;
0140 
0141     const auto images = DB::ImageDB::instance()->files();
0142     for (const DB::FileName &fileName : images) {
0143         const DB::ImageInfoPtr info = DB::ImageDB::instance()->info(fileName);
0144         const DB::MD5 md5 = info->MD5Sum();
0145         m_matches[md5].append(fileName);
0146     }
0147 
0148     bool anyFound = false;
0149     for (QMap<DB::MD5, DB::FileNameList>::const_iterator it = m_matches.constBegin();
0150          it != m_matches.constEnd(); ++it) {
0151         if (it.value().count() > 1) {
0152             addRow(it.key());
0153             anyFound = true;
0154         }
0155     }
0156 
0157     if (!anyFound) {
0158         tellThatNoDuplicatesWereFound();
0159     }
0160 
0161     updateSelectionCount();
0162 }
0163 
0164 void DuplicateMerger::addRow(const DB::MD5 &md5)
0165 {
0166     DuplicateMatch *match = new DuplicateMatch(m_matches[md5]);
0167     connect(match, &DuplicateMatch::selectionChanged, this, &DuplicateMerger::updateSelectionCount);
0168     m_scrollLayout->addWidget(match);
0169     m_selectors.append(match);
0170 }
0171 
0172 void DuplicateMerger::selectAll(bool b)
0173 {
0174     for (DuplicateMatch *selector : qAsConst(m_selectors)) {
0175         selector->setSelected(b);
0176     }
0177 }
0178 
0179 void DuplicateMerger::tellThatNoDuplicatesWereFound()
0180 {
0181     QLabel *label = new QLabel(i18n("No duplicates found"));
0182     QFont fnt = font();
0183     fnt.setPixelSize(30);
0184     label->setFont(fnt);
0185     m_scrollLayout->addWidget(label);
0186 
0187     m_selectAllButton->setEnabled(false);
0188     m_selectNoneButton->setEnabled(false);
0189     m_okButton->setEnabled(false);
0190 }
0191 
0192 } // namespace MainWindow
0193 
0194 // vi:expandtab:tabstop=4 shiftwidth=4:
0195 
0196 #include "moc_DuplicateMerger.cpp"