1 | /* |
2 | Copyright 2008 Ingo Klöcker <kloecker@kde.org> |
3 | Copyright 2010 Laurent Montel <montel@kde.org> |
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 <akonadi/changerecorder.h> |
26 | #include <akonadi/collectionfetchscope.h> |
27 | #include <akonadi/collectionfilterproxymodel.h> |
28 | #include <akonadi/entityrightsfiltermodel.h> |
29 | #include <akonadi/entitytreemodel.h> |
30 | #include <akonadi/entitytreeview.h> |
31 | #include <akonadi/session.h> |
32 | #include <akonadi/collectioncreatejob.h> |
33 | #include <akonadi/collectionutils_p.h> |
34 | |
35 | #include <QHeaderView> |
36 | #include <QLabel> |
37 | #include <QVBoxLayout> |
38 | #include <QCheckBox> |
39 | |
40 | #include <KLineEdit> |
41 | #include <KLocalizedString> |
42 | #include <KInputDialog> |
43 | #include <KMessageBox> |
44 | |
45 | using namespace Akonadi; |
46 | |
47 | class CollectionDialog::Private |
48 | { |
49 | public: |
50 | Private(QAbstractItemModel *customModel, CollectionDialog *parent, CollectionDialogOptions options) |
51 | : mParent(parent) |
52 | , mMonitor(0) |
53 | { |
54 | // setup GUI |
55 | QWidget *widget = mParent->mainWidget(); |
56 | QVBoxLayout *layout = new QVBoxLayout(widget); |
57 | layout->setContentsMargins(0, 0, 0, 0); |
58 | |
59 | mTextLabel = new QLabel; |
60 | layout->addWidget(mTextLabel); |
61 | mTextLabel->hide(); |
62 | |
63 | KLineEdit *filterCollectionLineEdit = new KLineEdit(widget); |
64 | filterCollectionLineEdit->setClearButtonShown(true); |
65 | filterCollectionLineEdit->setClickMessage(i18nc("@info/plain Displayed grayed-out inside the " |
66 | "textbox, verb to search" , "Search" )); |
67 | layout->addWidget(filterCollectionLineEdit); |
68 | |
69 | mView = new EntityTreeView; |
70 | mView->setDragDropMode(QAbstractItemView::NoDragDrop); |
71 | mView->header()->hide(); |
72 | layout->addWidget(mView); |
73 | |
74 | mUseByDefault = new QCheckBox(i18n("Use folder by default" )); |
75 | mUseByDefault->hide(); |
76 | layout->addWidget(mUseByDefault); |
77 | |
78 | mParent->enableButton(KDialog::Ok, false); |
79 | |
80 | // setup models |
81 | QAbstractItemModel *baseModel; |
82 | |
83 | if (customModel) { |
84 | baseModel = customModel; |
85 | } else { |
86 | mMonitor = new Akonadi::ChangeRecorder(mParent); |
87 | mMonitor->fetchCollection(true); |
88 | mMonitor->setCollectionMonitored(Akonadi::Collection::root()); |
89 | |
90 | EntityTreeModel *model = new EntityTreeModel(mMonitor, mParent); |
91 | model->setItemPopulationStrategy(EntityTreeModel::NoItemPopulation); |
92 | baseModel = model; |
93 | } |
94 | |
95 | mMimeTypeFilterModel = new CollectionFilterProxyModel(mParent); |
96 | mMimeTypeFilterModel->setSourceModel(baseModel); |
97 | mMimeTypeFilterModel->setExcludeVirtualCollections(true); |
98 | |
99 | mRightsFilterModel = new EntityRightsFilterModel(mParent); |
100 | mRightsFilterModel->setSourceModel(mMimeTypeFilterModel); |
101 | |
102 | mFilterCollection = new KRecursiveFilterProxyModel(mParent); |
103 | mFilterCollection->setDynamicSortFilter(true); |
104 | mFilterCollection->setSourceModel(mRightsFilterModel); |
105 | mFilterCollection->setFilterCaseSensitivity(Qt::CaseInsensitive); |
106 | mView->setModel(mFilterCollection); |
107 | |
108 | changeCollectionDialogOptions(options); |
109 | mParent->connect(filterCollectionLineEdit, SIGNAL(textChanged(QString)), |
110 | mParent, SLOT(slotFilterFixedString(QString))); |
111 | |
112 | mParent->connect(mView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), |
113 | mParent, SLOT(slotSelectionChanged())); |
114 | |
115 | mParent->connect(mView, SIGNAL(doubleClicked(QModelIndex)), |
116 | mParent, SLOT(accept())); |
117 | |
118 | mSelectionHandler = new AsyncSelectionHandler(mFilterCollection, mParent); |
119 | mParent->connect(mSelectionHandler, SIGNAL(collectionAvailable(QModelIndex)), |
120 | mParent, SLOT(slotCollectionAvailable(QModelIndex))); |
121 | readConfig(); |
122 | } |
123 | |
124 | ~Private() |
125 | { |
126 | writeConfig(); |
127 | } |
128 | |
129 | void slotCollectionAvailable(const QModelIndex &index) |
130 | { |
131 | mView->expandAll(); |
132 | mView->setCurrentIndex(index); |
133 | } |
134 | |
135 | void slotFilterFixedString(const QString &filter) |
136 | { |
137 | mFilterCollection->setFilterFixedString(filter); |
138 | if (mKeepTreeExpanded) { |
139 | mView->expandAll(); |
140 | } |
141 | } |
142 | |
143 | void readConfig() |
144 | { |
145 | KConfig config( QLatin1String( "akonadi_contactrc" ) ); |
146 | KConfigGroup group( &config, QLatin1String( "CollectionDialog" ) ); |
147 | const QSize size = group.readEntry( "Size" , QSize(800, 500) ); |
148 | if ( size.isValid() ) { |
149 | mParent->resize( size ); |
150 | } |
151 | } |
152 | |
153 | void writeConfig() |
154 | { |
155 | KConfig config( QLatin1String( "akonadi_contactrc" ) ); |
156 | KConfigGroup group( &config, QLatin1String( "CollectionDialog" ) ); |
157 | group.writeEntry( "Size" , mParent->size() ); |
158 | group.sync(); |
159 | } |
160 | |
161 | CollectionDialog *mParent; |
162 | |
163 | ChangeRecorder *mMonitor; |
164 | CollectionFilterProxyModel *mMimeTypeFilterModel; |
165 | EntityRightsFilterModel *mRightsFilterModel; |
166 | EntityTreeView *mView; |
167 | AsyncSelectionHandler *mSelectionHandler; |
168 | QLabel *mTextLabel; |
169 | bool mAllowToCreateNewChildCollection; |
170 | bool mKeepTreeExpanded; |
171 | KRecursiveFilterProxyModel *mFilterCollection; |
172 | QCheckBox *mUseByDefault; |
173 | |
174 | void slotSelectionChanged(); |
175 | void slotAddChildCollection(); |
176 | void slotCollectionCreationResult(KJob *job); |
177 | bool canCreateCollection(const Akonadi::Collection &parentCollection) const; |
178 | void changeCollectionDialogOptions(CollectionDialogOptions options); |
179 | |
180 | }; |
181 | |
182 | void CollectionDialog::Private::slotSelectionChanged() |
183 | { |
184 | mParent->enableButton(KDialog::Ok, mView->selectionModel()->selectedIndexes().count() > 0); |
185 | if (mAllowToCreateNewChildCollection) { |
186 | const Akonadi::Collection parentCollection = mParent->selectedCollection(); |
187 | const bool canCreateChildCollections = canCreateCollection(parentCollection); |
188 | |
189 | mParent->enableButton(KDialog::User1, (canCreateChildCollections && !parentCollection.isVirtual())); |
190 | if (parentCollection.isValid()) { |
191 | const bool canCreateItems = (parentCollection.rights() & Akonadi::Collection::CanCreateItem); |
192 | mParent->enableButton(KDialog::Ok, canCreateItems); |
193 | } |
194 | } |
195 | } |
196 | |
197 | void CollectionDialog::Private::changeCollectionDialogOptions(CollectionDialogOptions options) |
198 | { |
199 | mAllowToCreateNewChildCollection = (options & AllowToCreateNewChildCollection); |
200 | if (mAllowToCreateNewChildCollection) { |
201 | mParent->setButtons(Ok | Cancel | User1); |
202 | mParent->setButtonGuiItem(User1, KGuiItem(i18n("&New Subfolder..." ), QLatin1String("folder-new" ), |
203 | i18n("Create a new subfolder under the currently selected folder" ))); |
204 | mParent->enableButton(KDialog::User1, false); |
205 | connect(mParent, SIGNAL(user1Clicked()), mParent, SLOT(slotAddChildCollection())); |
206 | } |
207 | mKeepTreeExpanded = (options & KeepTreeExpanded); |
208 | if (mKeepTreeExpanded) { |
209 | mParent->connect(mRightsFilterModel, SIGNAL(rowsInserted(QModelIndex,int,int)), |
210 | mView, SLOT(expandAll()), Qt::UniqueConnection); |
211 | mView->expandAll(); |
212 | } |
213 | } |
214 | |
215 | bool CollectionDialog::Private::canCreateCollection(const Akonadi::Collection &parentCollection) const |
216 | { |
217 | if (!parentCollection.isValid()) { |
218 | return false; |
219 | } |
220 | |
221 | if ((parentCollection.rights() & Akonadi::Collection::CanCreateCollection)) { |
222 | const QStringList dialogMimeTypeFilter = mParent->mimeTypeFilter(); |
223 | const QStringList parentCollectionMimeTypes = parentCollection.contentMimeTypes(); |
224 | Q_FOREACH (const QString &mimetype, dialogMimeTypeFilter) { |
225 | if (parentCollectionMimeTypes.contains(mimetype)) { |
226 | return true; |
227 | } |
228 | } |
229 | return true; |
230 | } |
231 | return false; |
232 | } |
233 | |
234 | void CollectionDialog::Private::slotAddChildCollection() |
235 | { |
236 | const Akonadi::Collection parentCollection = mParent->selectedCollection(); |
237 | if (canCreateCollection(parentCollection)) { |
238 | const QString name = KInputDialog::getText(i18nc("@title:window" , "New Folder" ), |
239 | i18nc("@label:textbox, name of a thing" , "Name" ), |
240 | QString(), 0, mParent); |
241 | if (name.isEmpty()) { |
242 | return; |
243 | } |
244 | |
245 | Akonadi::Collection collection; |
246 | collection.setName(name); |
247 | collection.setParentCollection(parentCollection); |
248 | Akonadi::CollectionCreateJob *job = new Akonadi::CollectionCreateJob(collection); |
249 | connect(job, SIGNAL(result(KJob*)), mParent, SLOT(slotCollectionCreationResult(KJob*))); |
250 | } |
251 | } |
252 | |
253 | void CollectionDialog::Private::slotCollectionCreationResult(KJob *job) |
254 | { |
255 | if (job->error()) { |
256 | KMessageBox::error(mParent, i18n("Could not create folder: %1" , job->errorString()), |
257 | i18n("Folder creation failed" )); |
258 | } |
259 | } |
260 | |
261 | CollectionDialog::CollectionDialog(QWidget *parent) |
262 | : KDialog(parent) |
263 | , d(new Private(0, this, CollectionDialog::None)) |
264 | { |
265 | } |
266 | |
267 | CollectionDialog::CollectionDialog(QAbstractItemModel *model, QWidget *parent) |
268 | : KDialog(parent) |
269 | , d(new Private(model, this, CollectionDialog::None)) |
270 | { |
271 | } |
272 | |
273 | CollectionDialog::CollectionDialog(CollectionDialogOptions options, QAbstractItemModel *model, QWidget *parent) |
274 | : KDialog(parent) |
275 | , d(new Private(model, this, options)) |
276 | { |
277 | } |
278 | |
279 | CollectionDialog::~CollectionDialog() |
280 | { |
281 | delete d; |
282 | } |
283 | |
284 | Akonadi::Collection CollectionDialog::selectedCollection() const |
285 | { |
286 | if (selectionMode() == QAbstractItemView::SingleSelection) { |
287 | const QModelIndex index = d->mView->currentIndex(); |
288 | if (index.isValid()) { |
289 | return index.model()->data(index, EntityTreeModel::CollectionRole).value<Collection>(); |
290 | } |
291 | } |
292 | |
293 | return Collection(); |
294 | } |
295 | |
296 | Akonadi::Collection::List CollectionDialog::selectedCollections() const |
297 | { |
298 | Collection::List collections; |
299 | const QItemSelectionModel *selectionModel = d->mView->selectionModel(); |
300 | const QModelIndexList selectedIndexes = selectionModel->selectedIndexes(); |
301 | foreach (const QModelIndex &index, selectedIndexes) { |
302 | if (index.isValid()) { |
303 | const Collection collection = index.model()->data(index, EntityTreeModel::CollectionRole).value<Collection>(); |
304 | if (collection.isValid()) { |
305 | collections.append(collection); |
306 | } |
307 | } |
308 | } |
309 | |
310 | return collections; |
311 | } |
312 | |
313 | void CollectionDialog::setMimeTypeFilter(const QStringList &mimeTypes) |
314 | { |
315 | if (mimeTypeFilter() == mimeTypes) { |
316 | return; |
317 | } |
318 | |
319 | d->mMimeTypeFilterModel->clearFilters(); |
320 | d->mMimeTypeFilterModel->addMimeTypeFilters(mimeTypes); |
321 | |
322 | if (d->mMonitor) { |
323 | foreach (const QString &mimetype, mimeTypes) { |
324 | d->mMonitor->setMimeTypeMonitored(mimetype); |
325 | } |
326 | } |
327 | } |
328 | |
329 | QStringList CollectionDialog::mimeTypeFilter() const |
330 | { |
331 | return d->mMimeTypeFilterModel->mimeTypeFilters(); |
332 | } |
333 | |
334 | void CollectionDialog::setAccessRightsFilter(Collection::Rights rights) |
335 | { |
336 | if (accessRightsFilter() == rights) { |
337 | return; |
338 | } |
339 | d->mRightsFilterModel->setAccessRights(rights); |
340 | } |
341 | |
342 | Akonadi::Collection::Rights CollectionDialog::accessRightsFilter() const |
343 | { |
344 | return d->mRightsFilterModel->accessRights(); |
345 | } |
346 | |
347 | void CollectionDialog::setDescription(const QString &text) |
348 | { |
349 | d->mTextLabel->setText(text); |
350 | d->mTextLabel->show(); |
351 | } |
352 | |
353 | void CollectionDialog::setDefaultCollection(const Collection &collection) |
354 | { |
355 | d->mSelectionHandler->waitForCollection(collection); |
356 | } |
357 | |
358 | void CollectionDialog::setSelectionMode(QAbstractItemView::SelectionMode mode) |
359 | { |
360 | d->mView->setSelectionMode(mode); |
361 | } |
362 | |
363 | QAbstractItemView::SelectionMode CollectionDialog::selectionMode() const |
364 | { |
365 | return d->mView->selectionMode(); |
366 | } |
367 | |
368 | void CollectionDialog::changeCollectionDialogOptions(CollectionDialogOptions options) |
369 | { |
370 | d->changeCollectionDialogOptions(options); |
371 | } |
372 | |
373 | void CollectionDialog::setUseFolderByDefault(bool b) |
374 | { |
375 | d->mUseByDefault->setChecked(b); |
376 | d->mUseByDefault->show(); |
377 | } |
378 | |
379 | bool CollectionDialog::useFolderByDefault() const |
380 | { |
381 | return d->mUseByDefault->isChecked(); |
382 | } |
383 | |
384 | #include "moc_collectiondialog.cpp" |
385 | |