Akonadi

collectiondialog.cpp
1 /*
2  Copyright 2008 Ingo Klöcker <[email protected]>
3  Copyright 2010-2020 Laurent Montel <[email protected]>
4 
5  This library is free software; you can redistribute it and/or modify it
6  under the terms of the GNU Library General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or (at your
8  option) any later version.
9 
10  This library is distributed in the hope that it will be useful, but WITHOUT
11  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
13  License for more details.
14 
15  You should have received a copy of the GNU Library General Public License
16  along with this library; see the file COPYING.LIB. If not, write to the
17  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  02110-1301, USA.
19 */
20 
21 #include "collectiondialog.h"
22 
23 #include "asyncselectionhandler_p.h"
24 
25 #include "monitor.h"
26 #include "collectionfetchscope.h"
27 #include "collectionfilterproxymodel.h"
28 #include "entityrightsfiltermodel.h"
29 #include "entitytreemodel.h"
30 #include "entitytreeview.h"
31 #include "session.h"
32 #include "collectioncreatejob.h"
33 #include "collectionutils.h"
34 
35 #include <QHeaderView>
36 #include <QLabel>
37 #include <QVBoxLayout>
38 #include <QCheckBox>
39 #include <QDialogButtonBox>
40 
41 #include <QLineEdit>
42 #include <KLocalizedString>
43 #include <QInputDialog>
44 #include <QMessageBox>
45 #include <QPushButton>
46 #include <KConfig>
47 #include <KConfigGroup>
48 
49 using namespace Akonadi;
50 
51 class Q_DECL_HIDDEN CollectionDialog::Private
52 {
53 public:
54  Private(QAbstractItemModel *customModel, CollectionDialog *parent, CollectionDialogOptions options)
55  : mParent(parent)
56  {
57  // setup GUI
58  QVBoxLayout *layout = new QVBoxLayout(mParent);
59 
60  mTextLabel = new QLabel(mParent);
61  layout->addWidget(mTextLabel);
62  mTextLabel->hide();
63 
64  QLineEdit *filterCollectionLineEdit = new QLineEdit(mParent);
65  filterCollectionLineEdit->setClearButtonEnabled(true);
66  filterCollectionLineEdit->setPlaceholderText(i18nc("@info Displayed grayed-out inside the "
67  "textbox, verb to search", "Search"));
68  layout->addWidget(filterCollectionLineEdit);
69 
70  mView = new EntityTreeView(mParent);
71  mView->setDragDropMode(QAbstractItemView::NoDragDrop);
72  mView->header()->hide();
73  layout->addWidget(mView);
74 
75  mUseByDefault = new QCheckBox(i18n("Use folder by default"), mParent);
76  mUseByDefault->hide();
77  layout->addWidget(mUseByDefault);
78 
79  mButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, mParent);
80  mParent->connect(mButtonBox, &QDialogButtonBox::accepted, mParent, &QDialog::accept);
81  mParent->connect(mButtonBox, &QDialogButtonBox::rejected, mParent, &QDialog::reject);
82  layout->addWidget(mButtonBox);
83  mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
84 
85  // setup models
86  QAbstractItemModel *baseModel = nullptr;
87 
88  if (customModel) {
89  baseModel = customModel;
90  } else {
91  mMonitor = new Akonadi::Monitor(mParent);
92  mMonitor->setObjectName(QStringLiteral("CollectionDialogMonitor"));
93  mMonitor->fetchCollection(true);
94  mMonitor->setCollectionMonitored(Akonadi::Collection::root());
95 
96  EntityTreeModel *model = new EntityTreeModel(mMonitor, mParent);
99  baseModel = model;
100  }
101 
102  mMimeTypeFilterModel = new CollectionFilterProxyModel(mParent);
103  mMimeTypeFilterModel->setSourceModel(baseModel);
104  mMimeTypeFilterModel->setExcludeVirtualCollections(true);
105 
106  mRightsFilterModel = new EntityRightsFilterModel(mParent);
107  mRightsFilterModel->setSourceModel(mMimeTypeFilterModel);
108 
109  mFilterCollection = new QSortFilterProxyModel(mParent);
110  mFilterCollection->setRecursiveFilteringEnabled(true);
111  mFilterCollection->setSourceModel(mRightsFilterModel);
112  mFilterCollection->setFilterCaseSensitivity(Qt::CaseInsensitive);
113  mView->setModel(mFilterCollection);
114 
115  changeCollectionDialogOptions(options);
116  mParent->connect(filterCollectionLineEdit, &QLineEdit::textChanged,
117  mParent, [this](const QString &str) { slotFilterFixedString(str); });
118 
119  mParent->connect(mView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
120  mParent, SLOT(slotSelectionChanged()));
121 
122  mParent->connect(mView, SIGNAL(doubleClicked(QModelIndex)),
123  mParent, SLOT(slotDoubleClicked()));
124 
125  mSelectionHandler = new AsyncSelectionHandler(mFilterCollection, mParent);
126  mParent->connect(mSelectionHandler, &AsyncSelectionHandler::collectionAvailable,
127  mParent, [this](const QModelIndex &index) {slotCollectionAvailable(index);});
128  readConfig();
129  }
130 
131  ~Private()
132  {
133  writeConfig();
134  }
135 
136  void slotCollectionAvailable(const QModelIndex &index)
137  {
138  mView->expandAll();
139  mView->setCurrentIndex(index);
140  }
141 
142  void slotFilterFixedString(const QString &filter)
143  {
144  mFilterCollection->setFilterFixedString(filter);
145  if (mKeepTreeExpanded) {
146  mView->expandAll();
147  }
148  }
149 
150  void readConfig()
151  {
152  KConfig config(QStringLiteral("akonadi_contactrc"));
153  KConfigGroup group(&config, QStringLiteral("CollectionDialog"));
154  const QSize size = group.readEntry("Size", QSize(800, 500));
155  if (size.isValid()) {
156  mParent->resize(size);
157  }
158  }
159 
160  void writeConfig()
161  {
162  KConfig config(QStringLiteral("akonadi_contactrc"));
163  KConfigGroup group(&config, QStringLiteral("CollectionDialog"));
164  group.writeEntry("Size", mParent->size());
165  group.sync();
166  }
167 
168  CollectionDialog *mParent = nullptr;
169 
170  Monitor *mMonitor = nullptr;
171  CollectionFilterProxyModel *mMimeTypeFilterModel = nullptr;
172  EntityRightsFilterModel *mRightsFilterModel = nullptr;
173  EntityTreeView *mView = nullptr;
174  AsyncSelectionHandler *mSelectionHandler = nullptr;
175  QLabel *mTextLabel = nullptr;
176  QSortFilterProxyModel *mFilterCollection = nullptr;
177  QCheckBox *mUseByDefault = nullptr;
178  QStringList mContentMimeTypes;
179  QDialogButtonBox *mButtonBox = nullptr;
180  QPushButton *mNewSubfolderButton = nullptr;
181  bool mAllowToCreateNewChildCollection = false;
182  bool mKeepTreeExpanded = false;
183 
184  void slotDoubleClicked();
185  void slotSelectionChanged();
186  void slotAddChildCollection();
187  void slotCollectionCreationResult(KJob *job);
188  bool canCreateCollection(const Akonadi::Collection &parentCollection) const;
189  void changeCollectionDialogOptions(CollectionDialogOptions options);
190  bool canSelectCollection() const;
191 };
192 
193 void CollectionDialog::Private::slotDoubleClicked()
194 {
195  if (canSelectCollection()) {
196  mParent->accept();
197  }
198 }
199 
200 bool CollectionDialog::Private::canSelectCollection() const
201 {
202  bool result = (!mView->selectionModel()->selectedIndexes().isEmpty());
203  if (mAllowToCreateNewChildCollection) {
204  const Akonadi::Collection parentCollection = mParent->selectedCollection();
205 
206  if (parentCollection.isValid()) {
207  result = (parentCollection.rights() & Akonadi::Collection::CanCreateItem);
208  }
209  }
210  return result;
211 }
212 
213 void CollectionDialog::Private::slotSelectionChanged()
214 {
215  mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(!mView->selectionModel()->selectedIndexes().isEmpty());
216  if (mAllowToCreateNewChildCollection) {
217  const Akonadi::Collection parentCollection = mParent->selectedCollection();
218  const bool canCreateChildCollections = canCreateCollection(parentCollection);
219 
220  mNewSubfolderButton->setEnabled(canCreateChildCollections && !parentCollection.isVirtual());
221  if (parentCollection.isValid()) {
222  const bool canCreateItems = (parentCollection.rights() & Akonadi::Collection::CanCreateItem);
223  mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(canCreateItems);
224  }
225  }
226 }
227 
228 void CollectionDialog::Private::changeCollectionDialogOptions(CollectionDialogOptions options)
229 {
230  mAllowToCreateNewChildCollection = (options & AllowToCreateNewChildCollection);
231  if (mAllowToCreateNewChildCollection) {
232  mNewSubfolderButton = mButtonBox->addButton(i18n("&New Subfolder..."), QDialogButtonBox::NoRole);
233  mNewSubfolderButton->setIcon(QIcon::fromTheme(QStringLiteral("folder-new")));
234  mNewSubfolderButton->setToolTip(i18n("Create a new subfolder under the currently selected folder"));
235  mNewSubfolderButton->setEnabled(false);
236  connect(mNewSubfolderButton, SIGNAL(clicked(bool)), mParent, SLOT(slotAddChildCollection()));
237  }
238  mKeepTreeExpanded = (options & KeepTreeExpanded);
239  if (mKeepTreeExpanded) {
240  mParent->connect(mRightsFilterModel, &EntityRightsFilterModel::rowsInserted, mView, &EntityTreeView::expandAll, Qt::UniqueConnection);
241  mView->expandAll();
242  }
243 }
244 
245 bool CollectionDialog::Private::canCreateCollection(const Akonadi::Collection &parentCollection) const
246 {
247  if (!parentCollection.isValid()) {
248  return false;
249  }
250 
251  if ((parentCollection.rights() & Akonadi::Collection::CanCreateCollection)) {
252  const QStringList dialogMimeTypeFilter = mParent->mimeTypeFilter();
253  const QStringList parentCollectionMimeTypes = parentCollection.contentMimeTypes();
254  for (const QString &mimetype : dialogMimeTypeFilter) {
255  if (parentCollectionMimeTypes.contains(mimetype)) {
256  return true;
257  }
258  }
259  return true;
260  }
261  return false;
262 }
263 
264 void CollectionDialog::Private::slotAddChildCollection()
265 {
266  const Akonadi::Collection parentCollection = mParent->selectedCollection();
267  if (canCreateCollection(parentCollection)) {
268  const QString name = QInputDialog::getText(mParent, i18nc("@title:window", "New Folder"),
269  i18nc("@label:textbox, name of a thing", "Name"));
270  if (name.trimmed().isEmpty()) {
271  return;
272  }
273 
274  Akonadi::Collection collection;
275  collection.setName(name);
276  collection.setParentCollection(parentCollection);
277  if (!mContentMimeTypes.isEmpty()) {
278  collection.setContentMimeTypes(mContentMimeTypes);
279  }
281  connect(job, &Akonadi::CollectionCreateJob::result, mParent, [this](KJob *job) {slotCollectionCreationResult(job);});
282  }
283 }
284 
285 void CollectionDialog::Private::slotCollectionCreationResult(KJob *job)
286 {
287  if (job->error()) {
288  QMessageBox::critical(mParent, i18n("Folder creation failed"),
289  i18n("Could not create folder: %1", job->errorString()));
290  }
291 }
292 
294  : QDialog(parent)
295  , d(new Private(nullptr, this, CollectionDialog::None))
296 {
297 }
298 
300  : QDialog(parent)
301  , d(new Private(model, this, CollectionDialog::None))
302 {
303 }
304 
306  : QDialog(parent)
307  , d(new Private(model, this, options))
308 {
309 }
310 
312 {
313  delete d;
314 }
315 
317 {
318  if (selectionMode() == QAbstractItemView::SingleSelection) {
319  const QModelIndex index = d->mView->currentIndex();
320  if (index.isValid()) {
321  return index.model()->data(index, EntityTreeModel::CollectionRole).value<Collection>();
322  }
323  }
324 
325  return Collection();
326 }
327 
329 {
330  Collection::List collections;
331  const QItemSelectionModel *selectionModel = d->mView->selectionModel();
332  const QModelIndexList selectedIndexes = selectionModel->selectedIndexes();
333  for (const QModelIndex &index : selectedIndexes) {
334  if (index.isValid()) {
335  const Collection collection = index.model()->data(index, EntityTreeModel::CollectionRole).value<Collection>();
336  if (collection.isValid()) {
337  collections.append(collection);
338  }
339  }
340  }
341 
342  return collections;
343 }
344 
346 {
347  if (mimeTypeFilter() == mimeTypes) {
348  return;
349  }
350 
351  d->mMimeTypeFilterModel->clearFilters();
352  d->mMimeTypeFilterModel->addMimeTypeFilters(mimeTypes);
353 
354  if (d->mMonitor) {
355  for (const QString &mimetype : mimeTypes) {
356  d->mMonitor->setMimeTypeMonitored(mimetype);
357  }
358  }
359 }
360 
362 {
363  return d->mMimeTypeFilterModel->mimeTypeFilters();
364 }
365 
367 {
368  if (accessRightsFilter() == rights) {
369  return;
370  }
371  d->mRightsFilterModel->setAccessRights(rights);
372 }
373 
375 {
376  return d->mRightsFilterModel->accessRights();
377 }
378 
380 {
381  d->mTextLabel->setText(text);
382  d->mTextLabel->show();
383 }
384 
386 {
387  d->mSelectionHandler->waitForCollection(collection);
388 }
389 
390 void CollectionDialog::setSelectionMode(QAbstractItemView::SelectionMode mode)
391 {
392  d->mView->setSelectionMode(mode);
393 }
394 
395 QAbstractItemView::SelectionMode CollectionDialog::selectionMode() const
396 {
397  return d->mView->selectionMode();
398 }
399 
401 {
402  d->changeCollectionDialogOptions(options);
403 }
404 
406 {
407  d->mUseByDefault->setChecked(b);
408  d->mUseByDefault->show();
409 }
410 
412 {
413  return d->mUseByDefault->isChecked();
414 }
415 
417 {
418  d->mContentMimeTypes = mimetypes;
419 }
420 
421 #include "moc_collectiondialog.cpp"
void setAccessRightsFilter(Collection::Rights rights)
Sets the access rights that the listed collections shall match with.
Akonadi::Collection selectedCollection() const
Returns the selected collection if the selection mode is QAbstractItemView::SingleSelection.
void setSelectionMode(QAbstractItemView::SelectionMode mode)
Sets the selection mode.
bool isValid() const const
virtual void reject()
void setMimeTypeFilter(const QStringList &mimeTypes)
Sets the mime types any of which the selected collection(s) shall support.
bool isValid() const
Returns whether the collection is valid.
Definition: collection.cpp:137
void append(const T &value)
A proxy model that filters collections by mime type.
A collection selection dialog.
Akonadi::Collection::List selectedCollections() const
Returns the list of selected collections.
virtual QString errorString() const
Represents a collection of PIM items.
Definition: collection.h:76
Do not include items in the model.
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
T value() const const
void textChanged(const QString &text)
CollectionDialog(QWidget *parent=nullptr)
Creates a new collection dialog.
Can create new subcollections in this collection.
Definition: collection.h:98
Only retrieve collections for display, taking the local preference and enabled into account...
A proxy model that filters entities by access rights.
void setName(const QString &name)
Sets the i18n&#39;ed name of the collection.
Definition: collection.cpp:237
void setParentCollection(const Collection &parent)
Set the parent collection of this object.
Definition: collection.cpp:220
KSharedConfigPtr config()
QString name(StandardShortcut id)
void setEnabled(bool enabled)
Sets the collection&#39;s enabled state.
Definition: collection.cpp:374
bool isValid() const const
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
QAbstractItemView::SelectionMode selectionMode() const
Returns the selection mode.
void setContentMimeTypes(const QStringList &mimetypes)
Allow to specify collection content mimetype when we create new one.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
A view to show an item/collection tree provided by an EntityTreeModel.
bool isEmpty() const const
QString trimmed() const const
Can create new items in this collection.
Definition: collection.h:95
static Collection root()
Returns the root collection.
Definition: collection.cpp:303
virtual QVariant data(const QModelIndex &index, int role) const const =0
void setClearButtonEnabled(bool enable)
void setDefaultCollection(const Collection &collection)
Sets the collection that shall be selected by default.
void changeCollectionDialogOptions(CollectionDialogOptions options)
Change collection dialog options.
void setPlaceholderText(const QString &)
virtual void accept()
void setListFilter(Akonadi::CollectionFetchScope::ListFilter filter)
Sets the currently used listfilter.
QString getText(QWidget *parent, const QString &title, const QString &label, QLineEdit::EchoMode mode, const QString &text, bool *ok, Qt::WindowFlags flags, Qt::InputMethodHints inputMethodHints)
Rights rights() const
Returns the rights the user has on the collection.
Definition: collection.cpp:242
QStringList mimeTypeFilter() const
Returns the mime types any of which the selected collection(s) shall support.
Collection::Rights accessRightsFilter() const
Sets the access rights that the listed collections shall match with.
void setItemPopulationStrategy(ItemPopulationStrategy strategy)
Sets the item population strategy of the model.
void expandAll()
void setDescription(const QString &text)
Sets the text that will be shown in the dialog.
Monitors an item or collection for changes.
Definition: monitor.h:84
QString i18n(const char *text, const TYPE &arg...)
void readConfig()
const QAbstractItemModel * model() const const
Helper integration between Akonadi and Qt.
A model for collections and items together.
QMessageBox::StandardButton critical(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
QStringList contentMimeTypes() const
Returns a list of possible content mimetypes, e.g.
Definition: collection.cpp:256
~CollectionDialog()
Destroys the collection dialog.
QIcon fromTheme(const QString &name)
Job that creates a new collection in the Akonadi storage.
void result(KJob *job)
void rowsInserted(const QModelIndex &parent, int first, int last)
void setContentMimeTypes(const QStringList &types)
Sets the list of possible content mime types.
Definition: collection.cpp:261
bool isVirtual() const
Returns whether the collection is virtual, for example a search collection.
Definition: collection.cpp:364
int error() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon May 25 2020 22:46:08 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.