File indexing completed on 2025-01-05 04:54:20
0001 /* 0002 SPDX-FileCopyrightText: 2013-2024 Laurent Montel <montel@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 #include "knotecollectionconfigwidget.h" 0007 #include "attributes/showfoldernotesattribute.h" 0008 #include "notesharedglobalconfig.h" 0009 #include <Akonadi/ManageAccountWidget> 0010 #include <Akonadi/NoteUtils> 0011 0012 #include <Akonadi/CollectionFilterProxyModel> 0013 #include <Akonadi/CollectionModifyJob> 0014 #include <QInputDialog> 0015 #include <QSortFilterProxyModel> 0016 0017 #include <Akonadi/ChangeRecorder> 0018 #include <Akonadi/CollectionRequester> 0019 #include <Akonadi/EntityDisplayAttribute> 0020 #include <Akonadi/EntityTreeModel> 0021 #include <Akonadi/EntityTreeView> 0022 0023 #include <KMime/Message> 0024 0025 #include <KCheckableProxyModel> 0026 0027 #include "knotes_kcm_debug.h" 0028 #include <KLocalizedString> 0029 #include <KMessageBox> 0030 #include <QLineEdit> 0031 #include <QPushButton> 0032 0033 #include <QHBoxLayout> 0034 #include <QLabel> 0035 #include <QVBoxLayout> 0036 0037 KNoteCollectionDisplayProxyModel::KNoteCollectionDisplayProxyModel(QObject *parent) 0038 : QIdentityProxyModel(parent) 0039 { 0040 } 0041 0042 QVariant KNoteCollectionDisplayProxyModel::data(const QModelIndex &index, int role) const 0043 { 0044 if (role == Qt::CheckStateRole) { 0045 if (index.isValid()) { 0046 const auto collection = data(index, Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>(); 0047 if (mDisplayCollection.contains(collection)) { 0048 return mDisplayCollection.value(collection) ? Qt::Checked : Qt::Unchecked; 0049 } else { 0050 const auto attr = collection.attribute<NoteShared::ShowFolderNotesAttribute>(); 0051 if (attr) { 0052 return Qt::Checked; 0053 } 0054 return Qt::Unchecked; 0055 } 0056 } 0057 } 0058 return QIdentityProxyModel::data(index, role); 0059 } 0060 0061 bool KNoteCollectionDisplayProxyModel::setData(const QModelIndex &index, const QVariant &value, int role) 0062 { 0063 if (role == Qt::CheckStateRole) { 0064 if (index.isValid()) { 0065 const auto collection = data(index, Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>(); 0066 mDisplayCollection[collection] = (value == Qt::Checked); 0067 Q_EMIT dataChanged(index, index); 0068 return true; 0069 } 0070 } 0071 0072 return QIdentityProxyModel::setData(index, value, role); 0073 } 0074 0075 Qt::ItemFlags KNoteCollectionDisplayProxyModel::flags(const QModelIndex &index) const 0076 { 0077 if (index.isValid()) { 0078 return QIdentityProxyModel::flags(index) | Qt::ItemIsUserCheckable; 0079 } else { 0080 return QIdentityProxyModel::flags(index); 0081 } 0082 } 0083 0084 QHash<Akonadi::Collection, bool> KNoteCollectionDisplayProxyModel::displayCollection() const 0085 { 0086 return mDisplayCollection; 0087 } 0088 0089 KNoteCollectionConfigWidget::KNoteCollectionConfigWidget(QWidget *parent) 0090 : QWidget(parent) 0091 { 0092 auto mainLayout = new QHBoxLayout(this); 0093 mainLayout->setContentsMargins(0, 0, 0, 0); 0094 0095 auto tabWidget = new QTabWidget; 0096 mainLayout->addWidget(tabWidget); 0097 0098 auto collectionWidget = new QWidget; 0099 auto vbox = new QVBoxLayout; 0100 collectionWidget->setLayout(vbox); 0101 tabWidget->addTab(collectionWidget, i18n("Folders")); 0102 0103 auto label = new QLabel(i18n("Select which KNotes folders to show:")); 0104 vbox->addWidget(label); 0105 0106 // Create a new change recorder. 0107 mChangeRecorder = new Akonadi::ChangeRecorder(this); 0108 mChangeRecorder->setMimeTypeMonitored(Akonadi::NoteUtils::noteMimeType()); 0109 mChangeRecorder->fetchCollection(true); 0110 mChangeRecorder->setAllMonitored(true); 0111 0112 mModel = new Akonadi::EntityTreeModel(mChangeRecorder, this); 0113 // Set the model to show only collections, not items. 0114 mModel->setItemPopulationStrategy(Akonadi::EntityTreeModel::NoItemPopulation); 0115 0116 auto mimeTypeProxy = new Akonadi::CollectionFilterProxyModel(this); 0117 mimeTypeProxy->setExcludeVirtualCollections(true); 0118 mimeTypeProxy->addMimeTypeFilters(QStringList() << Akonadi::NoteUtils::noteMimeType()); 0119 mimeTypeProxy->setSourceModel(mModel); 0120 0121 mDisplayNotifierProxyModel = new KNoteCollectionDisplayProxyModel(this); 0122 mDisplayNotifierProxyModel->setSourceModel(mimeTypeProxy); 0123 connect(mDisplayNotifierProxyModel, &KNoteCollectionDisplayProxyModel::dataChanged, this, &KNoteCollectionConfigWidget::slotDataChanged); 0124 0125 connect(mModel, &Akonadi::EntityTreeModel::collectionTreeFetched, this, &KNoteCollectionConfigWidget::slotCollectionsInserted); 0126 0127 mCollectionFilter = new QSortFilterProxyModel(this); 0128 mCollectionFilter->setRecursiveFilteringEnabled(true); 0129 mCollectionFilter->setSourceModel(mDisplayNotifierProxyModel); 0130 mCollectionFilter->setDynamicSortFilter(true); 0131 mCollectionFilter->setFilterCaseSensitivity(Qt::CaseInsensitive); 0132 0133 auto searchLine = new QLineEdit(this); 0134 searchLine->setPlaceholderText(i18n("Search...")); 0135 searchLine->setClearButtonEnabled(true); 0136 connect(searchLine, &QLineEdit::textChanged, this, &KNoteCollectionConfigWidget::slotSetCollectionFilter); 0137 0138 vbox->addWidget(searchLine); 0139 0140 mFolderView = new Akonadi::EntityTreeView(this); 0141 mFolderView->setDragEnabled(false); 0142 mFolderView->setEditTriggers(QAbstractItemView::NoEditTriggers); 0143 mFolderView->setAlternatingRowColors(true); 0144 vbox->addWidget(mFolderView); 0145 0146 mFolderView->setModel(mCollectionFilter); 0147 connect(mFolderView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KNoteCollectionConfigWidget::slotUpdateButtons); 0148 0149 auto hbox = new QHBoxLayout; 0150 vbox->addLayout(hbox); 0151 0152 auto button = new QPushButton(i18n("&Select All"), this); 0153 connect(button, &QPushButton::clicked, this, &KNoteCollectionConfigWidget::slotSelectAllCollections); 0154 hbox->addWidget(button); 0155 0156 button = new QPushButton(i18n("&Unselect All"), this); 0157 connect(button, &QPushButton::clicked, this, &KNoteCollectionConfigWidget::slotUnselectAllCollections); 0158 hbox->addWidget(button); 0159 hbox->addStretch(1); 0160 0161 mRenameCollection = new QPushButton(i18n("Rename notes..."), this); 0162 connect(mRenameCollection, &QPushButton::clicked, this, &KNoteCollectionConfigWidget::slotRenameCollection); 0163 hbox->addWidget(mRenameCollection); 0164 0165 vbox->addWidget(new QLabel(i18nc("@info", "Select the folder where the note will be saved:"))); 0166 mDefaultSaveFolder = new Akonadi::CollectionRequester(Akonadi::Collection(NoteShared::NoteSharedGlobalConfig::self()->defaultFolder())); 0167 mDefaultSaveFolder->setMimeTypeFilter(QStringList() << Akonadi::NoteUtils::noteMimeType()); 0168 mDefaultSaveFolder->setContentMimeTypes(QStringList() << QStringLiteral("application/x-vnd.akonadi.note") << QStringLiteral("text/x-vnd.akonadi.note") 0169 << QStringLiteral("inode/directory")); 0170 Akonadi::CollectionDialog::CollectionDialogOptions options; 0171 options |= Akonadi::CollectionDialog::AllowToCreateNewChildCollection; 0172 options |= Akonadi::CollectionDialog::KeepTreeExpanded; 0173 mDefaultSaveFolder->changeCollectionDialogOptions(options); 0174 connect(mDefaultSaveFolder, &Akonadi::CollectionRequester::collectionChanged, this, &KNoteCollectionConfigWidget::slotDataChanged); 0175 0176 vbox->addWidget(mDefaultSaveFolder); 0177 0178 auto accountWidget = new QWidget; 0179 auto vboxAccountWidget = new QVBoxLayout; 0180 accountWidget->setLayout(vboxAccountWidget); 0181 0182 auto manageAccountWidget = new Akonadi::ManageAccountWidget(this); 0183 manageAccountWidget->setDescriptionLabelText(i18n("Notes accounts")); 0184 vboxAccountWidget->addWidget(manageAccountWidget); 0185 0186 manageAccountWidget->setMimeTypeFilter(QStringList() << Akonadi::NoteUtils::noteMimeType()); 0187 manageAccountWidget->setCapabilityFilter(QStringList() << QStringLiteral("Resource")); // show only resources, no agents 0188 tabWidget->addTab(accountWidget, i18n("Accounts")); 0189 slotUpdateButtons(); 0190 } 0191 0192 KNoteCollectionConfigWidget::~KNoteCollectionConfigWidget() = default; 0193 0194 void KNoteCollectionConfigWidget::slotUpdateButtons() 0195 { 0196 mRenameCollection->setEnabled(mFolderView->selectionModel()->hasSelection()); 0197 } 0198 0199 void KNoteCollectionConfigWidget::slotRenameCollection() 0200 { 0201 const QModelIndexList rows = mFolderView->selectionModel()->selectedRows(); 0202 0203 if (rows.size() != 1) { 0204 return; 0205 } 0206 0207 QModelIndex idx = rows.at(0); 0208 0209 QString title = idx.data().toString(); 0210 0211 auto col = idx.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>(); 0212 Q_ASSERT(col.isValid()); 0213 if (!col.isValid()) { 0214 return; 0215 } 0216 0217 bool ok; 0218 const QString name = QInputDialog::getText(this, i18n("Rename Notes"), i18n("Name:"), QLineEdit::Normal, title, &ok); 0219 0220 if (ok) { 0221 if (col.hasAttribute<Akonadi::EntityDisplayAttribute>() && !col.attribute<Akonadi::EntityDisplayAttribute>()->displayName().isEmpty()) { 0222 col.attribute<Akonadi::EntityDisplayAttribute>()->setDisplayName(name); 0223 } else if (!name.isEmpty()) { 0224 col.setName(name); 0225 } 0226 0227 auto job = new Akonadi::CollectionModifyJob(col, this); 0228 connect(job, &Akonadi::CollectionModifyJob::result, this, &KNoteCollectionConfigWidget::slotCollectionModifyFinished); 0229 job->start(); 0230 } 0231 } 0232 0233 void KNoteCollectionConfigWidget::slotCollectionModifyFinished(KJob *job) 0234 { 0235 if (job->error()) { 0236 KMessageBox::error(this, i18n("An error was occurred during renaming: %1", job->errorString()), i18n("Rename note")); 0237 } 0238 } 0239 0240 void KNoteCollectionConfigWidget::slotDataChanged() 0241 { 0242 Q_EMIT emitChanged(true); 0243 } 0244 0245 void KNoteCollectionConfigWidget::slotSetCollectionFilter(const QString &filter) 0246 { 0247 mCollectionFilter->setFilterWildcard(filter); 0248 mFolderView->expandAll(); 0249 } 0250 0251 void KNoteCollectionConfigWidget::slotSelectAllCollections() 0252 { 0253 forceStatus(QModelIndex(), true); 0254 Q_EMIT emitChanged(true); 0255 } 0256 0257 void KNoteCollectionConfigWidget::slotUnselectAllCollections() 0258 { 0259 forceStatus(QModelIndex(), false); 0260 Q_EMIT emitChanged(true); 0261 } 0262 0263 void KNoteCollectionConfigWidget::forceStatus(const QModelIndex &parent, bool status) 0264 { 0265 const int nbCol = mDisplayNotifierProxyModel->rowCount(parent); 0266 for (int i = 0; i < nbCol; ++i) { 0267 const QModelIndex child = mDisplayNotifierProxyModel->index(i, 0, parent); 0268 mDisplayNotifierProxyModel->setData(child, status ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole); 0269 forceStatus(child, status); 0270 } 0271 } 0272 0273 void KNoteCollectionConfigWidget::slotCollectionsInserted() 0274 { 0275 mFolderView->expandAll(); 0276 } 0277 0278 void KNoteCollectionConfigWidget::save() 0279 { 0280 updateCollectionsRecursive(); 0281 Akonadi::Collection col = mDefaultSaveFolder->collection(); 0282 if (col.isValid()) { 0283 NoteShared::NoteSharedGlobalConfig::self()->setDefaultFolder(col.id()); 0284 NoteShared::NoteSharedGlobalConfig::self()->save(); 0285 } 0286 } 0287 0288 void KNoteCollectionConfigWidget::updateCollectionsRecursive() 0289 { 0290 QHashIterator<Akonadi::Collection, bool> i(mDisplayNotifierProxyModel->displayCollection()); 0291 while (i.hasNext()) { 0292 i.next(); 0293 Akonadi::Collection collection = i.key(); 0294 Akonadi::CollectionModifyJob *modifyJob = nullptr; 0295 auto attr = collection.attribute<NoteShared::ShowFolderNotesAttribute>(); 0296 const bool selected = i.value(); 0297 if (selected && !attr) { 0298 attr = collection.attribute<NoteShared::ShowFolderNotesAttribute>(Akonadi::Collection::AddIfMissing); 0299 modifyJob = new Akonadi::CollectionModifyJob(collection); 0300 modifyJob->setProperty("AttributeAdded", true); 0301 } else if (!selected && attr) { 0302 collection.removeAttribute<NoteShared::ShowFolderNotesAttribute>(); 0303 modifyJob = new Akonadi::CollectionModifyJob(collection); 0304 modifyJob->setProperty("AttributeAdded", false); 0305 } 0306 0307 if (modifyJob) { 0308 connect(modifyJob, &Akonadi::CollectionModifyJob::finished, this, &KNoteCollectionConfigWidget::slotModifyJobDone); 0309 } 0310 } 0311 } 0312 0313 void KNoteCollectionConfigWidget::slotModifyJobDone(KJob *job) 0314 { 0315 auto modifyJob = qobject_cast<Akonadi::CollectionModifyJob *>(job); 0316 if (modifyJob && job->error()) { 0317 if (job->property("AttributeAdded").toBool()) { 0318 qCWarning(KNOTES_MODULES_LOG) << "Failed to append ShowFolderNotesAttribute to collection" << modifyJob->collection().id() << ":" 0319 << job->errorString(); 0320 } else { 0321 qCWarning(KNOTES_MODULES_LOG) << "Failed to remove ShowFolderNotesAttribute from collection" << modifyJob->collection().id() << ":" 0322 << job->errorString(); 0323 } 0324 } 0325 } 0326 0327 #include "moc_knotecollectionconfigwidget.cpp"