Akonadi

collectionview.cpp
1 /*
2  SPDX-FileCopyrightText: 2006-2007 Volker Krause <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "collectionview.h"
8 
9 #include "collection.h"
10 #include "controlgui.h"
11 #include "entitytreemodel.h"
12 #include "akonadiwidgets_debug.h"
13 
14 #include <KLocalizedString>
15 #include <KXMLGUIFactory>
16 #include <kxmlguiwindow.h>
17 
18 #include <QAction>
19 #include <QMimeData>
20 #include <QTimer>
21 #include <QUrlQuery>
22 #include <QApplication>
23 #include <QDragMoveEvent>
24 #include <QHeaderView>
25 #include <QMenu>
26 #include <QUrl>
27 
28 using namespace Akonadi;
29 
33 class Q_DECL_HIDDEN CollectionView::Private
34 {
35 public:
36  explicit Private(CollectionView *parent)
37  : mParent(parent)
38  {
39  }
40 
41  void init();
42  void dragExpand();
43  void itemClicked(const QModelIndex &index);
44  void itemCurrentChanged(const QModelIndex &index);
45  bool hasParent(const QModelIndex &idx, Collection::Id parentId) const;
46 
47  CollectionView *const mParent;
48  QModelIndex dragOverIndex;
49  QTimer dragExpandTimer;
50 
51  KXMLGUIClient *xmlGuiClient = nullptr;
52 };
53 
54 void CollectionView::Private::init()
55 {
56  mParent->header()->setSectionsClickable(true);
57  mParent->header()->setStretchLastSection(false);
58 
59  mParent->setSortingEnabled(true);
60  mParent->sortByColumn(0, Qt::AscendingOrder);
61  mParent->setEditTriggers(QAbstractItemView::EditKeyPressed);
62  mParent->setAcceptDrops(true);
63  mParent->setDropIndicatorShown(true);
64  mParent->setDragDropMode(DragDrop);
65  mParent->setDragEnabled(true);
66 
67  dragExpandTimer.setSingleShot(true);
68  mParent->connect(&dragExpandTimer, &QTimer::timeout, mParent, [this]() { dragExpand(); });
69 
70  mParent->connect(mParent, &QAbstractItemView::clicked, mParent, [this](const QModelIndex &mi) { itemClicked(mi); });
71 
73 }
74 
75 bool CollectionView::Private::hasParent(const QModelIndex &idx, Collection::Id parentId) const
76 {
77  QModelIndex idx2 = idx;
78  while (idx2.isValid()) {
79  if (mParent->model()->data(idx2, EntityTreeModel::CollectionIdRole).toLongLong() == parentId) {
80  return true;
81  }
82 
83  idx2 = idx2.parent();
84  }
85  return false;
86 }
87 
88 void CollectionView::Private::dragExpand()
89 {
90  mParent->setExpanded(dragOverIndex, true);
91  dragOverIndex = QModelIndex();
92 }
93 
94 void CollectionView::Private::itemClicked(const QModelIndex &index)
95 {
96  if (!index.isValid()) {
97  return;
98  }
99 
100  const Collection collection = index.model()->data(index, EntityTreeModel::CollectionRole).value<Collection>();
101  if (!collection.isValid()) {
102  return;
103  }
104 
105  Q_EMIT mParent->clicked(collection);
106 }
107 
108 void CollectionView::Private::itemCurrentChanged(const QModelIndex &index)
109 {
110  if (!index.isValid()) {
111  return;
112  }
113 
114  const Collection collection = index.model()->data(index, EntityTreeModel::CollectionRole).value<Collection>();
115  if (!collection.isValid()) {
116  return;
117  }
118 
119  Q_EMIT mParent->currentChanged(collection);
120 }
121 
123  : QTreeView(parent)
124  , d(new Private(this))
125 {
126  d->init();
127 }
128 
130  : QTreeView(parent)
131  , d(new Private(this))
132 {
133  d->xmlGuiClient = xmlGuiClient;
134  d->init();
135 }
136 
138 {
139  delete d;
140 }
141 
142 void CollectionView::setModel(QAbstractItemModel *model)
143 {
144  QTreeView::setModel(model);
145  header()->setStretchLastSection(true);
146 
148  this, [this](const QModelIndex &mi) { d->itemCurrentChanged(mi); });
149 }
150 
151 void CollectionView::dragMoveEvent(QDragMoveEvent *event)
152 {
153  QModelIndex index = indexAt(event->pos());
154  if (d->dragOverIndex != index) {
155  d->dragExpandTimer.stop();
156  if (index.isValid() && !isExpanded(index) && itemsExpandable()) {
157  d->dragExpandTimer.start(QApplication::startDragTime());
158  d->dragOverIndex = index;
159  }
160  }
161 
162  // Check if the collection under the cursor accepts this data type
163  const QStringList supportedContentTypes = model()->data(index, EntityTreeModel::CollectionRole).value<Collection>().contentMimeTypes();
164  const QMimeData *mimeData = event->mimeData();
165  if (!mimeData) {
166  return;
167  }
168  const QList<QUrl> urls = mimeData->urls();
169  for (const QUrl &url : urls) {
170 
171  const Collection collection = Collection::fromUrl(url);
172  if (collection.isValid()) {
173  if (!supportedContentTypes.contains(QLatin1String("inode/directory"))) {
174  break;
175  }
176 
177  // Check if we don't try to drop on one of the children
178  if (d->hasParent(index, collection.id())) {
179  break;
180  }
181  } else {
182  const QList<QPair<QString, QString> > query = QUrlQuery(url).queryItems();
183  const int numberOfQuery(query.count());
184  for (int i = 0; i < numberOfQuery; ++i) {
185  if (query.at(i).first == QLatin1String("type")) {
186  const QString type = query.at(i).second;
187  if (!supportedContentTypes.contains(type)) {
188  break;
189  }
190  }
191  }
192  }
193 
195  return;
196  }
197 
198  event->setDropAction(Qt::IgnoreAction);
199 }
200 
201 void CollectionView::dragLeaveEvent(QDragLeaveEvent *event)
202 {
203  d->dragExpandTimer.stop();
204  d->dragOverIndex = QModelIndex();
206 }
207 
208 void CollectionView::dropEvent(QDropEvent *event)
209 {
210  d->dragExpandTimer.stop();
211  d->dragOverIndex = QModelIndex();
212 
213  // open a context menu offering different drop actions (move, copy and cancel)
214  // TODO If possible, hide non available actions ...
215  QMenu popup(this);
216  QAction *moveDropAction = popup.addAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18n("&Move here"));
217  QAction *copyDropAction = popup.addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("&Copy here"));
218  popup.addSeparator();
219  popup.addAction(QIcon::fromTheme(QStringLiteral("process-stop")), i18n("Cancel"));
220 
221  QAction *activatedAction = popup.exec(QCursor::pos());
222  if (activatedAction == moveDropAction) {
223  event->setDropAction(Qt::MoveAction);
224  } else if (activatedAction == copyDropAction) {
225  event->setDropAction(Qt::CopyAction);
226  } else {
227  return;
228  }
229 
230  QTreeView::dropEvent(event);
231 }
232 
233 void CollectionView::contextMenuEvent(QContextMenuEvent *event)
234 {
235  if (!d->xmlGuiClient) {
236  return;
237  }
238  QMenu *popup = static_cast<QMenu *>(d->xmlGuiClient->factory()->container(
239  QStringLiteral("akonadi_collectionview_contextmenu"), d->xmlGuiClient));
240  if (popup) {
241  popup->exec(event->globalPos());
242  }
243 }
244 
246 {
247  d->xmlGuiClient = xmlGuiClient;
248 }
249 
250 #include "moc_collectionview.cpp"
virtual void dragMoveEvent(QDragMoveEvent *event) override
bool isValid() const
Returns whether the collection is valid.
Definition: collection.cpp:124
QItemSelectionModel * selectionModel() const const
QList< QPair< QString, QString > > queryItems(QUrl::ComponentFormattingOptions encoding) const const
Represents a collection of PIM items.
Definition: collection.h:63
A view to show a collection tree provided by a EntityTreeModel.
qint64 Id
Describes the unique id type.
Definition: collection.h:69
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)
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:250
int count(const T &value) const const
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()
QCA_EXPORT void init()
QString i18n(const char *text, const TYPE &arg...)
void currentChanged(const QModelIndex &current, const QModelIndex &previous)
virtual bool event(QEvent *event) override
int startDragTime()
Id id() const
Returns the unique identifier of the collection.
Definition: collection.cpp:99
const QAbstractItemModel * model() const const
QPoint pos()
QList< QUrl > urls() const const
AscendingOrder
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
void clicked(const QModelIndex &index)
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
IgnoreAction
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:270
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Thu Oct 22 2020 23:20:54 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.