• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepimlibs API Reference
  • KDE Home
  • Contact Us
 

akonadi

  • sources
  • kde-4.14
  • kdepimlibs
  • akonadi
dragdropmanager.cpp
1 /*
2  Copyright (c) 2009 Stephen Kelly <steveire@gmail.com>
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 "dragdropmanager_p.h"
21 #include "specialcollectionattribute_p.h"
22 #include "collectionutils_p.h"
23 
24 #include <QApplication>
25 #include <QDropEvent>
26 #include <QMenu>
27 #include <QDrag>
28 
29 #include <KDE/KIcon>
30 #include <KDE/KLocalizedString>
31 #include <KDE/KUrl>
32 
33 #include "akonadi/collection.h"
34 #include "akonadi/entitytreemodel.h"
35 
36 using namespace Akonadi;
37 
38 DragDropManager::DragDropManager(QAbstractItemView *view)
39  : mShowDropActionMenu(true)
40  , mIsManualSortingActive(false)
41  , m_view(view)
42 {
43 }
44 
45 Akonadi::Collection DragDropManager::currentDropTarget(QDropEvent *event) const
46 {
47  const QModelIndex index = m_view->indexAt(event->pos());
48  Collection collection = m_view->model()->data(index, EntityTreeModel::CollectionRole).value<Collection>();
49  if (!collection.isValid()) {
50  const Item item = m_view->model()->data(index, EntityTreeModel::ItemRole).value<Item>();
51  if (item.isValid()) {
52  collection = m_view->model()->data(index.parent(), EntityTreeModel::CollectionRole).value<Collection>();
53  }
54  }
55 
56  return collection;
57 }
58 
59 bool DragDropManager::dropAllowed(QDragMoveEvent *event) const
60 {
61  // Check if the collection under the cursor accepts this data type
62  const Collection targetCollection = currentDropTarget(event);
63  if (targetCollection.isValid()) {
64  const QStringList supportedContentTypes = targetCollection.contentMimeTypes();
65 
66  const QMimeData *data = event->mimeData();
67  const KUrl::List urls = KUrl::List::fromMimeData(data);
68  foreach (const KUrl &url, urls) {
69  const Collection collection = Collection::fromUrl(url);
70  if (collection.isValid()) {
71  if (!supportedContentTypes.contains(Collection::mimeType()) &&
72  !supportedContentTypes.contains(Collection::virtualMimeType())) {
73  break;
74  }
75 
76  // Check if we don't try to drop on one of the children
77  if (hasAncestor(m_view->indexAt(event->pos()), collection.id())) {
78  break;
79  }
80  } else { // This is an item.
81  const QString type = url.queryItems()[QString::fromLatin1("type")];
82  if (!supportedContentTypes.contains(type)) {
83  break;
84  }
85  }
86 
87  return true;
88  }
89  }
90 
91  return false;
92 }
93 
94 bool DragDropManager::hasAncestor(const QModelIndex &_index, Collection::Id parentId) const
95 {
96  QModelIndex index(_index);
97  while (index.isValid()) {
98  if (m_view->model()->data(index, EntityTreeModel::CollectionIdRole).toLongLong() == parentId) {
99  return true;
100  }
101 
102  index = index.parent();
103  }
104 
105  return false;
106 }
107 
108 bool DragDropManager::processDropEvent(QDropEvent *event, bool &menuCanceled, bool dropOnItem)
109 {
110  const Collection targetCollection = currentDropTarget(event);
111  if (!targetCollection.isValid()) {
112  return false;
113  }
114 
115  if (!mIsManualSortingActive && !dropOnItem) {
116  return false;
117  }
118 
119  const QStringList supportedContentTypes = targetCollection.contentMimeTypes();
120 
121  const QMimeData *data = event->mimeData();
122  const KUrl::List urls = KUrl::List::fromMimeData(data);
123  foreach (const KUrl &url, urls) {
124  const Collection collection = Collection::fromUrl(url);
125  if (!collection.isValid()) {
126  if (!dropOnItem) {
127  return false;
128  }
129  }
130  }
131 
132  int actionCount = 0;
133  Qt::DropAction defaultAction;
134  // TODO check if the source supports moving
135 
136  bool moveAllowed, copyAllowed, linkAllowed;
137  moveAllowed = copyAllowed = linkAllowed = false;
138 
139  if ((targetCollection.rights() & (Collection::CanCreateCollection | Collection::CanCreateItem)) &&
140  (event->possibleActions() & Qt::MoveAction)) {
141  moveAllowed = true;
142  }
143  if ((targetCollection.rights() & (Collection::CanCreateCollection | Collection::CanCreateItem)) &&
144  (event->possibleActions() & Qt::CopyAction)) {
145  copyAllowed = true;
146  }
147 
148  if ((targetCollection.rights() & Collection::CanLinkItem) &&
149  (event->possibleActions() & Qt::LinkAction)) {
150  linkAllowed = true;
151  }
152 
153  if (mIsManualSortingActive && !dropOnItem) {
154  moveAllowed = true;
155  copyAllowed = false;
156  linkAllowed = false;
157  }
158 
159  if (!moveAllowed && !copyAllowed && !linkAllowed) {
160  kDebug() << "Cannot drop here:" << event->possibleActions() << m_view->model()->supportedDragActions() << m_view->model()->supportedDropActions();
161  return false;
162  }
163 
164  // first check whether the user pressed a modifier key to select a specific action
165  if ((QApplication::keyboardModifiers() & Qt::ControlModifier) &&
166  (QApplication::keyboardModifiers() & Qt::ShiftModifier)) {
167  if (linkAllowed) {
168  defaultAction = Qt::LinkAction;
169  actionCount = 1;
170  } else {
171  return false;
172  }
173  } else if ((QApplication::keyboardModifiers() & Qt::ControlModifier)) {
174  if (copyAllowed) {
175  defaultAction = Qt::CopyAction;
176  actionCount = 1;
177  } else {
178  return false;
179  }
180  } else if ((QApplication::keyboardModifiers() & Qt::ShiftModifier)) {
181  if (moveAllowed) {
182  defaultAction = Qt::MoveAction;
183  actionCount = 1;
184  } else {
185  return false;
186  }
187  }
188 
189  if (actionCount == 1) {
190  kDebug() << "Selecting drop action" << defaultAction << ", there are no other possibilities";
191  event->setDropAction(defaultAction);
192  return true;
193  }
194 
195  if (!mShowDropActionMenu) {
196  if (moveAllowed) {
197  defaultAction = Qt::MoveAction;
198  } else if (copyAllowed) {
199  defaultAction = Qt::CopyAction;
200  } else if (linkAllowed) {
201  defaultAction = Qt::LinkAction;
202  } else {
203  return false;
204  }
205  event->setDropAction(defaultAction);
206  return true;
207  }
208 
209  // otherwise show up a menu to allow the user to select an action
210  QMenu popup(m_view);
211  QAction *moveDropAction = 0;
212  QAction *copyDropAction = 0;
213  QAction *linkAction = 0;
214  QString sequence;
215 
216  if (moveAllowed) {
217  sequence = QKeySequence(Qt::ShiftModifier).toString();
218  sequence.chop(1); // chop superfluous '+'
219  moveDropAction = popup.addAction(KIcon(QString::fromLatin1("go-jump")), i18n("&Move Here") + QLatin1Char('\t') + sequence);
220  }
221 
222  if (copyAllowed) {
223  sequence = QKeySequence(Qt::ControlModifier).toString();
224  sequence.chop(1); // chop superfluous '+'
225  copyDropAction = popup.addAction(KIcon(QString::fromLatin1("edit-copy")), i18n("&Copy Here") + QLatin1Char('\t') + sequence);
226  }
227 
228  if (linkAllowed) {
229  sequence = QKeySequence(Qt::ControlModifier + Qt::ShiftModifier).toString();
230  sequence.chop(1); // chop superfluous '+'
231  linkAction = popup.addAction(KIcon(QLatin1String("edit-link")), i18n("&Link Here") + QLatin1Char('\t') + sequence);
232  }
233 
234  popup.addSeparator();
235  popup.addAction(KIcon(QString::fromLatin1("process-stop")), i18n("C&ancel") + QLatin1Char('\t') + QKeySequence(Qt::Key_Escape).toString());
236 
237  QAction *activatedAction = popup.exec(QCursor::pos());
238  if (!activatedAction) {
239  menuCanceled = true;
240  return false;
241  } else if (activatedAction == moveDropAction) {
242  event->setDropAction(Qt::MoveAction);
243  } else if (activatedAction == copyDropAction) {
244  event->setDropAction(Qt::CopyAction);
245  } else if (activatedAction == linkAction) {
246  event->setDropAction(Qt::LinkAction);
247  } else {
248  menuCanceled = true;
249  return false;
250  }
251  return true;
252 }
253 
254 void DragDropManager::startDrag(Qt::DropActions supportedActions)
255 {
256  QModelIndexList indexes;
257  bool sourceDeletable = true;
258  foreach (const QModelIndex &index, m_view->selectionModel()->selectedRows()) {
259  if (!m_view->model()->flags(index).testFlag(Qt::ItemIsDragEnabled)) {
260  continue;
261  }
262 
263  if (sourceDeletable) {
264  Collection source = index.data(EntityTreeModel::CollectionRole).value<Collection>();
265  if (!source.isValid()) {
266  // index points to an item
267  source = index.data(EntityTreeModel::ParentCollectionRole).value<Collection>();
268  sourceDeletable = source.rights() & Collection::CanDeleteItem;
269  } else {
270  // index points to a collection
271  sourceDeletable = (source.rights() & Collection::CanDeleteCollection) && !source.hasAttribute<SpecialCollectionAttribute>() && !source.isVirtual();
272  }
273  }
274  indexes.append(index);
275  }
276 
277  if (indexes.isEmpty()) {
278  return;
279  }
280 
281  QMimeData *mimeData = m_view->model()->mimeData(indexes);
282  if (!mimeData) {
283  return;
284  }
285 
286  QDrag *drag = new QDrag(m_view);
287  drag->setMimeData(mimeData);
288  if (indexes.size() > 1) {
289  drag->setPixmap(KIcon(QLatin1String("document-multiple")).pixmap(QSize(22, 22)));
290  } else {
291  QPixmap pixmap = indexes.first().data(Qt::DecorationRole).value<QIcon>().pixmap(QSize(22, 22));
292  if (pixmap.isNull()) {
293  pixmap = KIcon(QLatin1String("text-plain")).pixmap(QSize(22, 22));
294  }
295  drag->setPixmap(pixmap);
296  }
297 
298  if (!sourceDeletable) {
299  supportedActions &= ~Qt::MoveAction;
300  }
301 
302  Qt::DropAction defaultAction = Qt::IgnoreAction;
303  if ((QApplication::keyboardModifiers() & Qt::ControlModifier) &&
304  (QApplication::keyboardModifiers() & Qt::ShiftModifier)) {
305  defaultAction = Qt::LinkAction;
306  } else if ((QApplication::keyboardModifiers() & Qt::ControlModifier)) {
307  defaultAction = Qt::CopyAction;
308  } else if ((QApplication::keyboardModifiers() & Qt::ShiftModifier)) {
309  defaultAction = Qt::MoveAction;
310  }
311 
312  drag->exec(supportedActions, defaultAction);
313 }
314 
315 bool DragDropManager::showDropActionMenu() const
316 {
317  return mShowDropActionMenu;
318 }
319 
320 void DragDropManager::setShowDropActionMenu(bool show)
321 {
322  mShowDropActionMenu = show;
323 }
324 
325 bool DragDropManager::isManualSortingActive() const
326 {
327  return mIsManualSortingActive;
328 }
329 
330 void DragDropManager::setManualSortingActive(bool active)
331 {
332  mIsManualSortingActive = active;
333 }
QModelIndex
QAbstractItemView
QApplication::keyboardModifiers
Qt::KeyboardModifiers keyboardModifiers()
QDropEvent::possibleActions
Qt::DropActions possibleActions() const
QDrag::setMimeData
void setMimeData(QMimeData *data)
QDragMoveEvent
QDrag::setPixmap
void setPixmap(const QPixmap &pixmap)
Akonadi::Collection
Represents a collection of PIM items.
Definition: collection.h:75
QDropEvent::pos
const QPoint & pos() const
QStringList::contains
bool contains(const QString &str, Qt::CaseSensitivity cs) const
Akonadi::SpecialCollectionAttribute
An Attribute that stores the special collection type of a collection.
Definition: specialcollectionattribute_p.h:39
QVariant::value
T value() const
QString::chop
void chop(int n)
QMimeData
QDrag::exec
Qt::DropAction exec(QFlags< Qt::DropAction > supportedActions)
QModelIndex::isValid
bool isValid() const
QDropEvent
QDrag
QString
QModelIndex::parent
QModelIndex parent() const
Akonadi::Entity::id
Id id() const
Returns the unique identifier of the entity.
Definition: entity.cpp:72
QStringList
QPixmap
Akonadi::Collection::rights
Rights rights() const
Returns the rights the user has on the collection.
Definition: collection.cpp:99
QMenu
QPixmap::isNull
bool isNull() const
QSize
QLatin1Char
QCursor::pos
QPoint pos()
QKeySequence::toString
QString toString(SequenceFormat format) const
QTest::toString
char * toString(const T &value)
QModelIndex::data
QVariant data(int role) const
QLatin1String
QKeySequence
Qt::DropActions
typedef DropActions
Akonadi::Entity::hasAttribute
bool hasAttribute(const QByteArray &name) const
Returns true if the entity has an attribute of the given type name, false otherwise.
Definition: entity.cpp:148
QAction
Akonadi::Collection::contentMimeTypes
QStringList contentMimeTypes() const
Returns a list of possible content mimetypes, e.g.
Definition: collection.cpp:115
QString::fromLatin1
QString fromLatin1(const char *str, int size)
Akonadi::Entity::isValid
bool isValid() const
Returns whether the entity is valid.
Definition: entity.cpp:97
QString::data
QChar * data()
QIcon
Akonadi::Collection::isVirtual
bool isVirtual() const
Returns whether the collection is virtual, for example a search collection.
Definition: collection.cpp:261
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:38:02 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi

Skip menu "akonadi"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Modules
  • Related Pages

kdepimlibs API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal