File indexing completed on 2024-05-19 05:00:22

0001 /*
0002     SPDX-FileCopyrightText: 2008 Tobias Koenig <tokoe@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "kcmtrash.h"
0008 
0009 #include <QCheckBox>
0010 #include <QComboBox>
0011 #include <QDialog>
0012 #include <QDoubleSpinBox>
0013 #include <QFormLayout>
0014 #include <QIcon>
0015 #include <QJsonDocument>
0016 #include <QLabel>
0017 #include <QListWidget>
0018 #include <QListWidgetItem>
0019 #include <QSpinBox>
0020 #include <QStorageInfo>
0021 
0022 #include <KConfig>
0023 #include <KConfigGroup>
0024 #include <KFormat>
0025 #include <KIO/SpecialJob>
0026 #include <KLocalizedString>
0027 #include <KPluginFactory>
0028 #include <kio/simplejob.h>
0029 
0030 K_PLUGIN_CLASS_WITH_JSON(TrashConfigModule, "kcm_trash.json")
0031 
0032 static constexpr int SPECIAL_TRASH_DIRECTORIES = 4;
0033 
0034 TrashConfigModule::TrashConfigModule(QObject *parent, const KPluginMetaData &data)
0035     : KCModule(parent, data)
0036     , trashInitialize(false)
0037 {
0038     QByteArray specialData;
0039     QDataStream stream(&specialData, QIODevice::WriteOnly);
0040     stream << SPECIAL_TRASH_DIRECTORIES;
0041     auto job = KIO::special(QUrl(QStringLiteral("trash:")), specialData);
0042 
0043     readConfig();
0044 
0045     connect(job, &KJob::finished, [job, this]() {
0046         auto doc = QJsonDocument::fromJson(job->metaData().value(QStringLiteral("TRASH_DIRECTORIES")).toLocal8Bit());
0047         const auto map = doc.object().toVariantMap();
0048         for (auto it = map.begin(); it != map.end(); it++) {
0049             m_trashMap.insert(it.key().toInt(), it.value().toString());
0050         }
0051         setupGui();
0052         useTypeChanged();
0053         trashChanged(0);
0054 
0055         connect(mUseTimeLimit, &QAbstractButton::toggled, this, &TrashConfigModule::markAsChanged);
0056         connect(mUseTimeLimit, &QAbstractButton::toggled, this, &TrashConfigModule::useTypeChanged);
0057         connect(mDays, qOverload<int>(&QSpinBox::valueChanged), this, &TrashConfigModule::markAsChanged);
0058         connect(mUseSizeLimit, &QAbstractButton::toggled, this, &TrashConfigModule::markAsChanged);
0059         connect(mUseSizeLimit, &QAbstractButton::toggled, this, &TrashConfigModule::useTypeChanged);
0060         connect(mPercent, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &TrashConfigModule::percentChanged);
0061         connect(mPercent, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &TrashConfigModule::markAsChanged);
0062         connect(mLimitReachedAction, qOverload<int>(&QComboBox::currentIndexChanged), this, &TrashConfigModule::markAsChanged);
0063 
0064         trashInitialize = true;
0065     });
0066 }
0067 
0068 TrashConfigModule::~TrashConfigModule()
0069 {
0070 }
0071 
0072 void TrashConfigModule::save()
0073 {
0074     if (!mCurrentTrash.isEmpty()) {
0075         ConfigEntry entry;
0076         entry.useTimeLimit = mUseTimeLimit->isChecked();
0077         entry.days = mDays->value();
0078         entry.useSizeLimit = mUseSizeLimit->isChecked();
0079         entry.percent = mPercent->value(), entry.actionType = mLimitReachedAction->currentIndex();
0080         mConfigMap.insert(mCurrentTrash, entry);
0081     }
0082 
0083     writeConfig();
0084 }
0085 
0086 void TrashConfigModule::defaults()
0087 {
0088     ConfigEntry entry;
0089     entry.useTimeLimit = false;
0090     entry.days = 7;
0091     entry.useSizeLimit = true;
0092     entry.percent = 10.0;
0093     entry.actionType = 0;
0094     mConfigMap.insert(mCurrentTrash, entry);
0095     trashInitialize = false;
0096     trashChanged(0);
0097 }
0098 
0099 void TrashConfigModule::percentChanged(double percent)
0100 {
0101     qint64 fullSize = 0;
0102     QStorageInfo storageInfo(mCurrentTrash);
0103     if (storageInfo.isValid() && storageInfo.isReady()) {
0104         fullSize = storageInfo.bytesTotal();
0105     }
0106 
0107     double size = static_cast<double>(fullSize / 100) * percent;
0108 
0109     KFormat format;
0110     mSizeLabel->setText(QLatin1Char('(') + format.formatByteSize(size, 2) + QLatin1Char(')'));
0111 }
0112 
0113 void TrashConfigModule::trashChanged(int value)
0114 {
0115     if (!mCurrentTrash.isEmpty() && trashInitialize) {
0116         ConfigEntry entry;
0117         entry.useTimeLimit = mUseTimeLimit->isChecked();
0118         entry.days = mDays->value();
0119         entry.useSizeLimit = mUseSizeLimit->isChecked();
0120         entry.percent = mPercent->value(), entry.actionType = mLimitReachedAction->currentIndex();
0121         mConfigMap.insert(mCurrentTrash, entry);
0122     }
0123 
0124     mCurrentTrash = m_trashMap[value];
0125     const auto currentTrashIt = mConfigMap.constFind(mCurrentTrash);
0126     if (currentTrashIt != mConfigMap.constEnd()) {
0127         const ConfigEntry &entry = *currentTrashIt;
0128         mUseTimeLimit->setChecked(entry.useTimeLimit);
0129         mDays->setValue(entry.days);
0130         mUseSizeLimit->setChecked(entry.useSizeLimit);
0131         mPercent->setValue(entry.percent);
0132         mLimitReachedAction->setCurrentIndex(entry.actionType);
0133     } else {
0134         mUseTimeLimit->setChecked(false);
0135         mDays->setValue(7);
0136         mUseSizeLimit->setChecked(true);
0137         mPercent->setValue(10.0);
0138         mLimitReachedAction->setCurrentIndex(0);
0139     }
0140     mDays->setSuffix(i18n(" days")); // missing in Qt: plural form handling
0141 
0142     percentChanged(mPercent->value());
0143 }
0144 
0145 void TrashConfigModule::useTypeChanged()
0146 {
0147     mDays->setEnabled(mUseTimeLimit->isChecked());
0148     mPercent->setEnabled(mUseSizeLimit->isChecked());
0149     mSizeLabel->setEnabled(mUseSizeLimit->isChecked());
0150 }
0151 
0152 void TrashConfigModule::readConfig()
0153 {
0154     KConfig config(QStringLiteral("ktrashrc"));
0155     mConfigMap.clear();
0156 
0157     const QStringList groups = config.groupList();
0158     for (const auto &name : groups) {
0159         if (name.startsWith(QLatin1Char('/'))) {
0160             const KConfigGroup group = config.group(name);
0161 
0162             ConfigEntry entry;
0163             entry.useTimeLimit = group.readEntry("UseTimeLimit", false);
0164             entry.days = group.readEntry("Days", 7);
0165             entry.useSizeLimit = group.readEntry("UseSizeLimit", true);
0166             entry.percent = group.readEntry("Percent", 10.0);
0167             entry.actionType = group.readEntry("LimitReachedAction", 0);
0168             mConfigMap.insert(name, entry);
0169         }
0170     }
0171 }
0172 
0173 void TrashConfigModule::writeConfig()
0174 {
0175     KConfig config(QStringLiteral("ktrashrc"));
0176 
0177     // first delete all existing groups
0178     const QStringList groups = config.groupList();
0179     for (const auto &name : groups) {
0180         if (name.startsWith(QLatin1Char('/'))) {
0181             config.deleteGroup(name);
0182         }
0183     }
0184 
0185     QMapIterator<QString, ConfigEntry> it(mConfigMap);
0186     while (it.hasNext()) {
0187         it.next();
0188         KConfigGroup group = config.group(it.key());
0189 
0190         const ConfigEntry entry = it.value();
0191         group.writeEntry("UseTimeLimit", entry.useTimeLimit);
0192         group.writeEntry("Days", entry.days);
0193         group.writeEntry("UseSizeLimit", entry.useSizeLimit);
0194         group.writeEntry("Percent", entry.percent);
0195         group.writeEntry("LimitReachedAction", entry.actionType);
0196     }
0197     config.sync();
0198 }
0199 
0200 void TrashConfigModule::setupGui()
0201 {
0202     QVBoxLayout *layout = new QVBoxLayout(widget());
0203 
0204 #ifdef Q_OS_OSX
0205     QLabel *infoText = new QLabel(i18n("<para>KDE's wastebin is configured to use the <b>Finder</b>'s Trash.<br></para>"));
0206     infoText->setWhatsThis(i18nc("@info:whatsthis",
0207                                  "<para>Emptying KDE's wastebin will remove only KDE's trash items, while<br>"
0208                                  "emptying the Trash through the Finder will delete everything.</para>"
0209                                  "<para>KDE's trash items will show up in a folder called KDE.trash, in the Trash can.</para>"));
0210     layout->addWidget(infoText);
0211 #endif
0212 
0213     if (m_trashMap.count() != 1) {
0214         // If we have multiple trashes, we setup a widget to choose
0215         // which trash to configure
0216         QListWidget *mountPoints = new QListWidget(widget());
0217         layout->addWidget(mountPoints);
0218 
0219         QMapIterator<int, QString> it(m_trashMap);
0220         while (it.hasNext()) {
0221             it.next();
0222 
0223             QString mountPoint;
0224             QStorageInfo storageInfo(it.value());
0225             if (storageInfo.isValid() && storageInfo.isReady()) {
0226                 mountPoint = storageInfo.rootPath();
0227             }
0228             auto item = new QListWidgetItem(QIcon(QStringLiteral("folder")), mountPoint);
0229             item->setData(Qt::UserRole, it.key());
0230 
0231             mountPoints->addItem(item);
0232         }
0233 
0234         mountPoints->setCurrentRow(0);
0235 
0236         connect(mountPoints, &QListWidget::currentItemChanged, this, [this](QListWidgetItem *item) {
0237             trashChanged(item->data(Qt::UserRole).toInt());
0238         });
0239     } else {
0240         mCurrentTrash = m_trashMap.value(0);
0241     }
0242 
0243     QFormLayout *formLayout = new QFormLayout();
0244     layout->addLayout(formLayout);
0245 
0246     QHBoxLayout *daysLayout = new QHBoxLayout();
0247 
0248     mUseTimeLimit = new QCheckBox(i18n("Delete files older than"), widget());
0249     mUseTimeLimit->setWhatsThis(
0250         xi18nc("@info:whatsthis",
0251                "<para>Check this box to allow <emphasis strong='true'>automatic deletion</emphasis> of files that are older than the value specified. "
0252                "Leave this disabled to <emphasis strong='true'>not</emphasis> automatically delete any items after a certain timespan</para>"));
0253     daysLayout->addWidget(mUseTimeLimit);
0254     mDays = new QSpinBox(widget());
0255 
0256     mDays->setRange(1, 365);
0257     mDays->setSingleStep(1);
0258     mDays->setSuffix(i18np(" day", " days", mDays->value()));
0259     mDays->setWhatsThis(xi18nc("@info:whatsthis",
0260                                "<para>Set the number of days that files can remain in the trash. "
0261                                "Any files older than this will be automatically deleted.</para>"));
0262     daysLayout->addWidget(mDays);
0263     daysLayout->addStretch();
0264     formLayout->addRow(i18n("Cleanup:"), daysLayout);
0265 
0266     QHBoxLayout *maximumSizeLayout = new QHBoxLayout();
0267     mUseSizeLimit = new QCheckBox(i18n("Limit to"), widget());
0268     mUseSizeLimit->setWhatsThis(xi18nc("@info:whatsthis",
0269                                        "<para>Check this box to limit the trash to the maximum amount of disk space that you specify below. "
0270                                        "Otherwise, it will be unlimited.</para>"));
0271     maximumSizeLayout->addWidget(mUseSizeLimit);
0272     formLayout->addRow(i18n("Size:"), maximumSizeLayout);
0273 
0274     mPercent = new QDoubleSpinBox(widget());
0275     mPercent->setRange(0.01, 100);
0276     mPercent->setDecimals(2);
0277     mPercent->setSingleStep(1);
0278     mPercent->setSuffix(QStringLiteral(" %"));
0279     mPercent->setWhatsThis(xi18nc("@info:whatsthis", "<para>This is the maximum percent of disk space that will be used for the trash.</para>"));
0280     maximumSizeLayout->addWidget(mPercent);
0281 
0282     mSizeLabel = new QLabel(widget());
0283     mSizeLabel->setWhatsThis(
0284         xi18nc("@info:whatsthis", "<para>This is the calculated amount of disk space that will be allowed for the trash, the maximum.</para>"));
0285     maximumSizeLayout->addWidget(mSizeLabel);
0286 
0287     mLimitReachedAction = new QComboBox();
0288     mLimitReachedAction->addItem(i18n("Show a Warning"));
0289     mLimitReachedAction->addItem(i18n("Delete Oldest Files From Trash"));
0290     mLimitReachedAction->addItem(i18n("Delete Biggest Files From Trash"));
0291     mLimitReachedAction->setWhatsThis(xi18nc("@info:whatsthis",
0292                                              "<para>When the size limit is reached, it will prefer to delete the type of files that you specify, first. "
0293                                              "If this is set to warn you, it will do so instead of automatically deleting files.</para>"));
0294     formLayout->addRow(i18n("Full Trash:"), mLimitReachedAction);
0295 
0296     layout->addStretch();
0297 }
0298 
0299 #include "kcmtrash.moc"