akonadi
dragdropmanager.cpp
00001 /* 00002 Copyright (c) 2009 Stephen Kelly <steveire@gmail.com> 00003 00004 This library is free software; you can redistribute it and/or modify it 00005 under the terms of the GNU Library General Public License as published by 00006 the Free Software Foundation; either version 2 of the License, or (at your 00007 option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, but WITHOUT 00010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00011 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00012 License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to the 00016 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00017 02110-1301, USA. 00018 */ 00019 00020 #include "dragdropmanager_p.h" 00021 #include "specialcollectionattribute_p.h" 00022 #include "collectionutils_p.h" 00023 00024 #include <QtGui/QApplication> 00025 #include <QtGui/QDropEvent> 00026 #include <QtGui/QMenu> 00027 00028 #include <KDE/KIcon> 00029 #include <KDE/KLocale> 00030 #include <KDE/KUrl> 00031 00032 #include "akonadi/collection.h" 00033 #include "akonadi/entitytreemodel.h" 00034 00035 using namespace Akonadi; 00036 00037 DragDropManager::DragDropManager( QAbstractItemView *view ) 00038 : mShowDropActionMenu( true ), m_view( view ) 00039 { 00040 } 00041 00042 Akonadi::Collection DragDropManager::currentDropTarget( QDropEvent *event ) const 00043 { 00044 const QModelIndex index = m_view->indexAt( event->pos() ); 00045 00046 Collection collection = m_view->model()->data( index, EntityTreeModel::CollectionRole ).value<Collection>(); 00047 if ( !collection.isValid() ) { 00048 const Item item = m_view->model()->data( index, EntityTreeModel::ItemRole ).value<Item>(); 00049 if ( item.isValid() ) 00050 collection = m_view->model()->data( index.parent(), EntityTreeModel::CollectionRole ).value<Collection>(); 00051 } 00052 00053 return collection; 00054 } 00055 00056 bool DragDropManager::dropAllowed( QDragMoveEvent *event ) const 00057 { 00058 // Check if the collection under the cursor accepts this data type 00059 const Collection targetCollection = currentDropTarget( event ); 00060 if ( targetCollection.isValid() ) { 00061 const QStringList supportedContentTypes = targetCollection.contentMimeTypes(); 00062 00063 const QMimeData *data = event->mimeData(); 00064 const KUrl::List urls = KUrl::List::fromMimeData( data ); 00065 foreach ( const KUrl &url, urls ) { 00066 const Collection collection = Collection::fromUrl( url ); 00067 if ( collection.isValid() ) { 00068 if ( !supportedContentTypes.contains( Collection::mimeType() ) ) 00069 break; 00070 00071 // Check if we don't try to drop on one of the children 00072 if ( hasAncestor( m_view->indexAt( event->pos() ), collection.id() ) ) 00073 break; 00074 } else { // This is an item. 00075 const QString type = url.queryItems()[ QString::fromLatin1( "type" ) ]; 00076 if ( !supportedContentTypes.contains( type ) ) 00077 break; 00078 } 00079 00080 return true; 00081 } 00082 } 00083 00084 return false; 00085 } 00086 00087 bool DragDropManager::hasAncestor( const QModelIndex &_index, Collection::Id parentId ) const 00088 { 00089 QModelIndex index( _index ); 00090 while ( index.isValid() ) { 00091 if ( m_view->model()->data( index, EntityTreeModel::CollectionIdRole ).toLongLong() == parentId ) 00092 return true; 00093 00094 index = index.parent(); 00095 } 00096 00097 return false; 00098 } 00099 00100 bool DragDropManager::processDropEvent( QDropEvent *event ) 00101 { 00102 const Collection targetCollection = currentDropTarget( event ); 00103 if ( !targetCollection.isValid() ) 00104 return false; 00105 00106 int actionCount = 0; 00107 Qt::DropAction defaultAction; 00108 // TODO check if the source supports moving 00109 00110 bool moveAllowed, copyAllowed, linkAllowed; 00111 moveAllowed = copyAllowed = linkAllowed = false; 00112 00113 if ( (targetCollection.rights() & (Collection::CanCreateCollection | Collection::CanCreateItem)) 00114 && (event->possibleActions() & Qt::MoveAction) ) { 00115 moveAllowed = true; 00116 } 00117 if ( (targetCollection.rights() & (Collection::CanCreateCollection | Collection::CanCreateItem)) 00118 && (event->possibleActions() & Qt::CopyAction) ) { 00119 copyAllowed = true; 00120 } 00121 00122 if ( (targetCollection.rights() & Collection::CanLinkItem) && (event->possibleActions() & Qt::LinkAction) ) { 00123 linkAllowed = true; 00124 } 00125 00126 if ( !moveAllowed && !copyAllowed && !linkAllowed ) { 00127 kDebug() << "Cannot drop here:" << event->possibleActions() << m_view->model()->supportedDragActions() << m_view->model()->supportedDropActions(); 00128 return false; 00129 } 00130 00131 // first check whether the user pressed a modifier key to select a specific action 00132 if ( (QApplication::keyboardModifiers() & Qt::ControlModifier) && 00133 (QApplication::keyboardModifiers() & Qt::ShiftModifier) ) { 00134 if ( linkAllowed ) { 00135 defaultAction = Qt::LinkAction; 00136 actionCount = 1; 00137 } else 00138 return false; 00139 } else if ( (QApplication::keyboardModifiers() & Qt::ControlModifier) ) { 00140 if ( copyAllowed ) { 00141 defaultAction = Qt::CopyAction; 00142 actionCount = 1; 00143 } else 00144 return false; 00145 } else if ( (QApplication::keyboardModifiers() & Qt::ShiftModifier) ) { 00146 if ( moveAllowed ) { 00147 defaultAction = Qt::MoveAction; 00148 actionCount = 1; 00149 } else 00150 return false; 00151 } 00152 00153 if ( actionCount == 1 ) { 00154 kDebug() << "Selecting drop action" << defaultAction << ", there are no other possibilities"; 00155 event->setDropAction( defaultAction ); 00156 return true; 00157 } 00158 00159 if ( !mShowDropActionMenu ) { 00160 if ( moveAllowed ) 00161 defaultAction = Qt::MoveAction; 00162 else if ( copyAllowed ) 00163 defaultAction = Qt::CopyAction; 00164 else if ( linkAllowed ) 00165 defaultAction = Qt::LinkAction; 00166 else 00167 return false; 00168 event->setDropAction( defaultAction ); 00169 return true; 00170 } 00171 00172 // otherwise show up a menu to allow the user to select an action 00173 QMenu popup( m_view ); 00174 QAction* moveDropAction = 0; 00175 QAction* copyDropAction = 0; 00176 QAction* linkAction = 0; 00177 QString sequence; 00178 00179 if ( moveAllowed ) { 00180 sequence = QKeySequence( Qt::ShiftModifier ).toString(); 00181 sequence.chop( 1 ); // chop superfluous '+' 00182 moveDropAction = popup.addAction( KIcon( QString::fromLatin1( "go-jump" ) ), i18n( "&Move Here" ) + QLatin1Char( '\t' ) + sequence ); 00183 } 00184 00185 if ( copyAllowed ) { 00186 sequence = QKeySequence( Qt::ControlModifier ).toString(); 00187 sequence.chop( 1 ); // chop superfluous '+' 00188 copyDropAction = popup.addAction( KIcon( QString::fromLatin1( "edit-copy" ) ), i18n( "&Copy Here" ) + QLatin1Char( '\t' ) + sequence ); 00189 } 00190 00191 if ( linkAllowed ) { 00192 sequence = QKeySequence( Qt::ControlModifier + Qt::ShiftModifier ).toString(); 00193 sequence.chop( 1 ); // chop superfluous '+' 00194 linkAction = popup.addAction( KIcon( QLatin1String( "edit-link" ) ), i18n( "&Link Here" ) + QLatin1Char( '\t' ) + sequence ); 00195 } 00196 00197 popup.addSeparator(); 00198 popup.addAction( KIcon( QString::fromLatin1( "process-stop" ) ), i18n( "C&ancel" ) + QLatin1Char( '\t' ) + QKeySequence( Qt::Key_Escape ).toString() ); 00199 00200 QAction *activatedAction = popup.exec( QCursor::pos() ); 00201 00202 if ( !activatedAction ) { 00203 return false; 00204 } else if ( activatedAction == moveDropAction ) { 00205 event->setDropAction( Qt::MoveAction ); 00206 } else if ( activatedAction == copyDropAction ) { 00207 event->setDropAction( Qt::CopyAction ); 00208 } else if ( activatedAction == linkAction ) { 00209 event->setDropAction( Qt::LinkAction ); 00210 } else { 00211 return false; 00212 } 00213 00214 return true; 00215 } 00216 00217 void DragDropManager::startDrag( Qt::DropActions supportedActions ) 00218 { 00219 QModelIndexList indexes; 00220 bool sourceDeletable = true; 00221 foreach ( const QModelIndex &index, m_view->selectionModel()->selectedRows() ) { 00222 if ( !m_view->model()->flags( index ).testFlag( Qt::ItemIsDragEnabled ) ) 00223 continue; 00224 00225 if ( sourceDeletable ) { 00226 Collection source = index.data( EntityTreeModel::CollectionRole ).value<Collection>(); 00227 if ( !source.isValid() ) { 00228 // index points to an item 00229 source = index.data( EntityTreeModel::ParentCollectionRole ).value<Collection>(); 00230 sourceDeletable = source.rights() & Collection::CanDeleteItem; 00231 } else { 00232 // index points to a collection 00233 sourceDeletable = ( source.rights() & Collection::CanDeleteCollection ) && !source.hasAttribute<SpecialCollectionAttribute>() && !CollectionUtils::isVirtual( source ); 00234 } 00235 } 00236 indexes.append( index ); 00237 } 00238 00239 if ( indexes.isEmpty() ) 00240 return; 00241 00242 QMimeData *mimeData = m_view->model()->mimeData( indexes ); 00243 if ( !mimeData ) 00244 return; 00245 00246 QDrag *drag = new QDrag( m_view ); 00247 drag->setMimeData( mimeData ); 00248 if ( indexes.size() > 1 ) { 00249 drag->setPixmap( KIcon( QLatin1String( "document-multiple" ) ).pixmap( QSize( 22, 22 ) ) ); 00250 } else { 00251 QPixmap pixmap = indexes.first().data( Qt::DecorationRole ).value<QIcon>().pixmap( QSize( 22, 22 ) ); 00252 if ( pixmap.isNull() ) 00253 pixmap = KIcon( QLatin1String( "text-plain" ) ).pixmap( QSize( 22, 22 ) ); 00254 drag->setPixmap( pixmap ); 00255 } 00256 00257 if ( !sourceDeletable ) 00258 supportedActions &= ~Qt::MoveAction; 00259 00260 Qt::DropAction defaultAction = Qt::IgnoreAction; 00261 if ( (QApplication::keyboardModifiers() & Qt::ControlModifier) && 00262 (QApplication::keyboardModifiers() & Qt::ShiftModifier) ) { 00263 defaultAction = Qt::LinkAction; 00264 } else if ( (QApplication::keyboardModifiers() & Qt::ControlModifier) ) { 00265 defaultAction = Qt::CopyAction; 00266 } else if ( (QApplication::keyboardModifiers() & Qt::ShiftModifier) ) { 00267 defaultAction = Qt::MoveAction; 00268 } 00269 00270 drag->exec( supportedActions, defaultAction ); 00271 } 00272 00273 bool DragDropManager::showDropActionMenu() const 00274 { 00275 return mShowDropActionMenu; 00276 } 00277 00278 void DragDropManager::setShowDropActionMenu( bool show ) 00279 { 00280 mShowDropActionMenu = show; 00281 }