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

akonadi

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

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