Akonadi

collectionview.cpp
1 /*
2  Copyright (c) 2006 - 2007 Volker Krause <[email protected]>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 
20 #include "collectionview.h"
21 
22 #include "collection.h"
23 #include "controlgui.h"
24 #include "entitytreemodel.h"
25 #include "akonadiwidgets_debug.h"
26 
27 #include <KLocalizedString>
28 #include <KXMLGUIFactory>
29 #include <kxmlguiwindow.h>
30 
31 #include <QAction>
32 #include <QMimeData>
33 #include <QTimer>
34 #include <QUrlQuery>
35 #include <QApplication>
36 #include <QDragMoveEvent>
37 #include <QHeaderView>
38 #include <QMenu>
39 #include <QUrl>
40 
41 using namespace Akonadi;
42 
46 class Q_DECL_HIDDEN CollectionView::Private
47 {
48 public:
49  Private(CollectionView *parent)
50  : mParent(parent)
51  {
52  }
53 
54  void init();
55  void dragExpand();
56  void itemClicked(const QModelIndex &index);
57  void itemCurrentChanged(const QModelIndex &index);
58  bool hasParent(const QModelIndex &idx, Collection::Id parentId);
59 
60  CollectionView *mParent = nullptr;
61  QModelIndex dragOverIndex;
62  QTimer dragExpandTimer;
63 
64  KXMLGUIClient *xmlGuiClient = nullptr;
65 };
66 
67 void CollectionView::Private::init()
68 {
69  mParent->header()->setSectionsClickable(true);
70  mParent->header()->setStretchLastSection(false);
71 
72  mParent->setSortingEnabled(true);
73  mParent->sortByColumn(0, Qt::AscendingOrder);
74  mParent->setEditTriggers(QAbstractItemView::EditKeyPressed);
75  mParent->setAcceptDrops(true);
76  mParent->setDropIndicatorShown(true);
77  mParent->setDragDropMode(DragDrop);
78  mParent->setDragEnabled(true);
79 
80  dragExpandTimer.setSingleShot(true);
81  mParent->connect(&dragExpandTimer, &QTimer::timeout, mParent, [this]() { dragExpand(); });
82 
83  mParent->connect(mParent, SIGNAL(clicked(QModelIndex)), mParent, SLOT(itemClicked(QModelIndex)));
84 
86 }
87 
88 bool CollectionView::Private::hasParent(const QModelIndex &idx, Collection::Id parentId)
89 {
90  QModelIndex idx2 = idx;
91  while (idx2.isValid()) {
92  if (mParent->model()->data(idx2, EntityTreeModel::CollectionIdRole).toLongLong() == parentId) {
93  return true;
94  }
95 
96  idx2 = idx2.parent();
97  }
98  return false;
99 }
100 
101 void CollectionView::Private::dragExpand()
102 {
103  mParent->setExpanded(dragOverIndex, true);
104  dragOverIndex = QModelIndex();
105 }
106 
107 void CollectionView::Private::itemClicked(const QModelIndex &index)
108 {
109  if (!index.isValid()) {
110  return;
111  }
112 
113  const Collection collection = index.model()->data(index, EntityTreeModel::CollectionRole).value<Collection>();
114  if (!collection.isValid()) {
115  return;
116  }
117 
118  Q_EMIT mParent->clicked(collection);
119 }
120 
121 void CollectionView::Private::itemCurrentChanged(const QModelIndex &index)
122 {
123  if (!index.isValid()) {
124  return;
125  }
126 
127  const Collection collection = index.model()->data(index, EntityTreeModel::CollectionRole).value<Collection>();
128  if (!collection.isValid()) {
129  return;
130  }
131 
132  Q_EMIT mParent->currentChanged(collection);
133 }
134 
136  : QTreeView(parent)
137  , d(new Private(this))
138 {
139  d->init();
140 }
141 
143  : QTreeView(parent)
144  , d(new Private(this))
145 {
146  d->xmlGuiClient = xmlGuiClient;
147  d->init();
148 }
149 
151 {
152  delete d;
153 }
154 
155 void CollectionView::setModel(QAbstractItemModel *model)
156 {
157  QTreeView::setModel(model);
158  header()->setStretchLastSection(true);
159 
161  this, SLOT(itemCurrentChanged(QModelIndex)));
162 }
163 
164 void CollectionView::dragMoveEvent(QDragMoveEvent *event)
165 {
166  QModelIndex index = indexAt(event->pos());
167  if (d->dragOverIndex != index) {
168  d->dragExpandTimer.stop();
169  if (index.isValid() && !isExpanded(index) && itemsExpandable()) {
170  d->dragExpandTimer.start(QApplication::startDragTime());
171  d->dragOverIndex = index;
172  }
173  }
174 
175  // Check if the collection under the cursor accepts this data type
176  const QStringList supportedContentTypes = model()->data(index, EntityTreeModel::CollectionRole).value<Collection>().contentMimeTypes();
177  const QMimeData *mimeData = event->mimeData();
178  if (!mimeData) {
179  return;
180  }
181  const QList<QUrl> urls = mimeData->urls();
182  for (const QUrl &url : urls) {
183 
184  const Collection collection = Collection::fromUrl(url);
185  if (collection.isValid()) {
186  if (!supportedContentTypes.contains(QLatin1String("inode/directory"))) {
187  break;
188  }
189 
190  // Check if we don't try to drop on one of the children
191  if (d->hasParent(index, collection.id())) {
192  break;
193  }
194  } else {
195  const QList<QPair<QString, QString> > query = QUrlQuery(url).queryItems();
196  const int numberOfQuery(query.count());
197  for (int i = 0; i < numberOfQuery; ++i) {
198  if (query.at(i).first == QLatin1String("type")) {
199  const QString type = query.at(i).second;
200  if (!supportedContentTypes.contains(type)) {
201  break;
202  }
203  }
204  }
205  }
206 
208  return;
209  }
210 
211  event->setDropAction(Qt::IgnoreAction);
212 }
213 
214 void CollectionView::dragLeaveEvent(QDragLeaveEvent *event)
215 {
216  d->dragExpandTimer.stop();
217  d->dragOverIndex = QModelIndex();
219 }
220 
221 void CollectionView::dropEvent(QDropEvent *event)
222 {
223  d->dragExpandTimer.stop();
224  d->dragOverIndex = QModelIndex();
225 
226  // open a context menu offering different drop actions (move, copy and cancel)
227  // TODO If possible, hide non available actions ...
228  QMenu popup(this);
229  QAction *moveDropAction = popup.addAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18n("&Move here"));
230  QAction *copyDropAction = popup.addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("&Copy here"));
231  popup.addSeparator();
232  popup.addAction(QIcon::fromTheme(QStringLiteral("process-stop")), i18n("Cancel"));
233 
234  QAction *activatedAction = popup.exec(QCursor::pos());
235  if (activatedAction == moveDropAction) {
236  event->setDropAction(Qt::MoveAction);
237  } else if (activatedAction == copyDropAction) {
238  event->setDropAction(Qt::CopyAction);
239  } else {
240  return;
241  }
242 
243  QTreeView::dropEvent(event);
244 }
245 
246 void CollectionView::contextMenuEvent(QContextMenuEvent *event)
247 {
248  if (!d->xmlGuiClient) {
249  return;
250  }
251  QMenu *popup = static_cast<QMenu *>(d->xmlGuiClient->factory()->container(
252  QStringLiteral("akonadi_collectionview_contextmenu"), d->xmlGuiClient));
253  if (popup) {
254  popup->exec(event->globalPos());
255  }
256 }
257 
259 {
260  d->xmlGuiClient = xmlGuiClient;
261 }
262 
263 #include "moc_collectionview.cpp"
virtual void dragMoveEvent(QDragMoveEvent *event) override
bool isValid() const
Returns whether the collection is valid.
Definition: collection.cpp:137
QItemSelectionModel * selectionModel() const const
QList< QPair< QString, QString > > queryItems(QUrl::ComponentFormattingOptions encoding) const const
Represents a collection of PIM items.
Definition: collection.h:76
A view to show a collection tree provided by a EntityTreeModel.
qint64 Id
Describes the unique id type.
Definition: collection.h:82
const T & at(int i) const const
QPoint pos() const const
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
T value() const const
~CollectionView() override
Destroys the collection view.
const QPoint & globalPos() const const
void setXmlGuiClient(KXMLGUIClient *xmlGuiClient)
Sets the KXMLGUIClient which the view is used in.
QAction * addAction(const QString &text)
QUICKADDONS_EXPORT void init()
void timeout()
bool isValid() const const
static void widgetNeedsAkonadi(QWidget *widget)
Disable the given widget when Akonadi is not operational and show an error overlay (given enough spac...
Definition: controlgui.cpp:266
int count(const T &value) const const
void currentChanged(const Akonadi::Collection &collection)
This signal is emitted whenever the current collection in the view has changed.
virtual QVariant data(const QModelIndex &index, int role) const const =0
QAction * addSeparator()
QModelIndex parent() const const
bool isExpanded(const QModelIndex &index) const const
QAction * exec()
QString i18n(const char *text, const TYPE &arg...)
virtual bool event(QEvent *event) override
int startDragTime()
Id id() const
Returns the unique identifier of the collection.
Definition: collection.cpp:112
const QAbstractItemModel * model() const const
QPoint pos()
QList< QUrl > urls() const const
Helper integration between Akonadi and Qt.
virtual void setModel(QAbstractItemModel *model) override
virtual void dropEvent(QDropEvent *event) override
CollectionView(QWidget *parent=nullptr)
Creates a new collection view.
virtual void dragLeaveEvent(QDragLeaveEvent *event) override
QIcon fromTheme(const QString &name)
QHeaderView * header() const const
QAbstractItemModel * model() const const
void setStretchLastSection(bool stretch)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
bool itemsExpandable() const const
virtual QModelIndex indexAt(const QPoint &point) const const override
static Collection fromUrl(const QUrl &url)
Creates a collection from the given url.
Definition: collection.cpp:283
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Tue May 26 2020 22:46:18 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.