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 ), mIsManualSortingActive( false ), 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 Collection collection = m_view->model()->data( index, EntityTreeModel::CollectionRole ).value<Collection>(); 00046 if ( !collection.isValid() ) { 00047 const Item item = m_view->model()->data( index, EntityTreeModel::ItemRole ).value<Item>(); 00048 if ( item.isValid() ) 00049 collection = m_view->model()->data( index.parent(), EntityTreeModel::CollectionRole ).value<Collection>(); 00050 } 00051 00052 return collection; 00053 } 00054 00055 bool DragDropManager::dropAllowed( QDragMoveEvent *event ) const 00056 { 00057 // Check if the collection under the cursor accepts this data type 00058 const Collection targetCollection = currentDropTarget( event ); 00059 if ( targetCollection.isValid() ) { 00060 const QStringList supportedContentTypes = targetCollection.contentMimeTypes(); 00061 00062 const QMimeData *data = event->mimeData(); 00063 const KUrl::List urls = KUrl::List::fromMimeData( data ); 00064 foreach ( const KUrl &url, urls ) { 00065 const Collection collection = Collection::fromUrl( url ); 00066 if ( collection.isValid() ) { 00067 if ( !supportedContentTypes.contains( Collection::mimeType() ) ) 00068 break; 00069 00070 // Check if we don't try to drop on one of the children 00071 if ( hasAncestor( m_view->indexAt( event->pos() ), collection.id() ) ) 00072 break; 00073 } else { // This is an item. 00074 const QString type = url.queryItems()[ QString::fromLatin1( "type" ) ]; 00075 if ( !supportedContentTypes.contains( type ) ) 00076 break; 00077 } 00078 00079 return true; 00080 } 00081 } 00082 00083 return false; 00084 } 00085 00086 bool DragDropManager::hasAncestor( const QModelIndex &_index, Collection::Id parentId ) const 00087 { 00088 QModelIndex index( _index ); 00089 while ( index.isValid() ) { 00090 if ( m_view->model()->data( index, EntityTreeModel::CollectionIdRole ).toLongLong() == parentId ) 00091 return true; 00092 00093 index = index.parent(); 00094 } 00095 00096 return false; 00097 } 00098 00099 bool DragDropManager::processDropEvent( QDropEvent *event, bool &menuCanceled, bool dropOnItem ) 00100 { 00101 const Collection targetCollection = currentDropTarget( event ); 00102 if ( !targetCollection.isValid() ) 00103 return false; 00104 00105 if ( !mIsManualSortingActive && !dropOnItem ) 00106 { 00107 return false; 00108 } 00109 00110 const QStringList supportedContentTypes = targetCollection.contentMimeTypes(); 00111 00112 const QMimeData *data = event->mimeData(); 00113 const KUrl::List urls = KUrl::List::fromMimeData( data ); 00114 foreach ( const KUrl &url, urls ) { 00115 const Collection collection = Collection::fromUrl( url ); 00116 if( !collection.isValid() ) { 00117 if ( !dropOnItem ) { 00118 return false; 00119 } 00120 } 00121 } 00122 00123 int actionCount = 0; 00124 Qt::DropAction defaultAction; 00125 // TODO check if the source supports moving 00126 00127 bool moveAllowed, copyAllowed, linkAllowed; 00128 moveAllowed = copyAllowed = linkAllowed = false; 00129 00130 if ( (targetCollection.rights() & (Collection::CanCreateCollection | Collection::CanCreateItem)) 00131 && (event->possibleActions() & Qt::MoveAction) ) { 00132 moveAllowed = true; 00133 } 00134 if ( (targetCollection.rights() & (Collection::CanCreateCollection | Collection::CanCreateItem)) 00135 && (event->possibleActions() & Qt::CopyAction) ) { 00136 copyAllowed = true; 00137 } 00138 00139 if ( (targetCollection.rights() & Collection::CanLinkItem) && (event->possibleActions() & Qt::LinkAction) ) { 00140 linkAllowed = true; 00141 } 00142 00143 if ( mIsManualSortingActive && !dropOnItem ) { 00144 moveAllowed = true; 00145 copyAllowed = false; 00146 linkAllowed = false; 00147 } 00148 00149 if ( !moveAllowed && !copyAllowed && !linkAllowed ) { 00150 kDebug() << "Cannot drop here:" << event->possibleActions() << m_view->model()->supportedDragActions() << m_view->model()->supportedDropActions(); 00151 return false; 00152 } 00153 00154 // first check whether the user pressed a modifier key to select a specific action 00155 if ( (QApplication::keyboardModifiers() & Qt::ControlModifier) && 00156 (QApplication::keyboardModifiers() & Qt::ShiftModifier) ) { 00157 if ( linkAllowed ) { 00158 defaultAction = Qt::LinkAction; 00159 actionCount = 1; 00160 } else 00161 return false; 00162 } else if ( (QApplication::keyboardModifiers() & Qt::ControlModifier) ) { 00163 if ( copyAllowed ) { 00164 defaultAction = Qt::CopyAction; 00165 actionCount = 1; 00166 } else 00167 return false; 00168 } else if ( (QApplication::keyboardModifiers() & Qt::ShiftModifier) ) { 00169 if ( moveAllowed ) { 00170 defaultAction = Qt::MoveAction; 00171 actionCount = 1; 00172 } else 00173 return false; 00174 } 00175 00176 if ( actionCount == 1 ) { 00177 kDebug() << "Selecting drop action" << defaultAction << ", there are no other possibilities"; 00178 event->setDropAction( defaultAction ); 00179 return true; 00180 } 00181 00182 if ( !mShowDropActionMenu ) { 00183 if ( moveAllowed ) 00184 defaultAction = Qt::MoveAction; 00185 else if ( copyAllowed ) 00186 defaultAction = Qt::CopyAction; 00187 else if ( linkAllowed ) 00188 defaultAction = Qt::LinkAction; 00189 else 00190 return false; 00191 event->setDropAction( defaultAction ); 00192 return true; 00193 } 00194 00195 // otherwise show up a menu to allow the user to select an action 00196 QMenu popup( m_view ); 00197 QAction* moveDropAction = 0; 00198 QAction* copyDropAction = 0; 00199 QAction* linkAction = 0; 00200 QString sequence; 00201 00202 if ( moveAllowed ) { 00203 sequence = QKeySequence( Qt::ShiftModifier ).toString(); 00204 sequence.chop( 1 ); // chop superfluous '+' 00205 moveDropAction = popup.addAction( KIcon( QString::fromLatin1( "go-jump" ) ), i18n( "&Move Here" ) + QLatin1Char( '\t' ) + sequence ); 00206 } 00207 00208 if ( copyAllowed ) { 00209 sequence = QKeySequence( Qt::ControlModifier ).toString(); 00210 sequence.chop( 1 ); // chop superfluous '+' 00211 copyDropAction = popup.addAction( KIcon( QString::fromLatin1( "edit-copy" ) ), i18n( "&Copy Here" ) + QLatin1Char( '\t' ) + sequence ); 00212 } 00213 00214 if ( linkAllowed ) { 00215 sequence = QKeySequence( Qt::ControlModifier + Qt::ShiftModifier ).toString(); 00216 sequence.chop( 1 ); // chop superfluous '+' 00217 linkAction = popup.addAction( KIcon( QLatin1String( "edit-link" ) ), i18n( "&Link Here" ) + QLatin1Char( '\t' ) + sequence ); 00218 } 00219 00220 popup.addSeparator(); 00221 popup.addAction( KIcon( QString::fromLatin1( "process-stop" ) ), i18n( "C&ancel" ) + QLatin1Char( '\t' ) + QKeySequence( Qt::Key_Escape ).toString() ); 00222 00223 QAction *activatedAction = popup.exec( QCursor::pos() ); 00224 if ( !activatedAction ) { 00225 menuCanceled = true; 00226 return false; 00227 } else if ( activatedAction == moveDropAction ) { 00228 event->setDropAction( Qt::MoveAction ); 00229 } else if ( activatedAction == copyDropAction ) { 00230 event->setDropAction( Qt::CopyAction ); 00231 } else if ( activatedAction == linkAction ) { 00232 event->setDropAction( Qt::LinkAction ); 00233 } else { 00234 menuCanceled = true; 00235 return false; 00236 } 00237 return true; 00238 } 00239 00240 void DragDropManager::startDrag( Qt::DropActions supportedActions ) 00241 { 00242 QModelIndexList indexes; 00243 bool sourceDeletable = true; 00244 foreach ( const QModelIndex &index, m_view->selectionModel()->selectedRows() ) { 00245 if ( !m_view->model()->flags( index ).testFlag( Qt::ItemIsDragEnabled ) ) 00246 continue; 00247 00248 if ( sourceDeletable ) { 00249 Collection source = index.data( EntityTreeModel::CollectionRole ).value<Collection>(); 00250 if ( !source.isValid() ) { 00251 // index points to an item 00252 source = index.data( EntityTreeModel::ParentCollectionRole ).value<Collection>(); 00253 sourceDeletable = source.rights() & Collection::CanDeleteItem; 00254 } else { 00255 // index points to a collection 00256 sourceDeletable = ( source.rights() & Collection::CanDeleteCollection ) && !source.hasAttribute<SpecialCollectionAttribute>() && !CollectionUtils::isVirtual( source ); 00257 } 00258 } 00259 indexes.append( index ); 00260 } 00261 00262 if ( indexes.isEmpty() ) 00263 return; 00264 00265 QMimeData *mimeData = m_view->model()->mimeData( indexes ); 00266 if ( !mimeData ) 00267 return; 00268 00269 QDrag *drag = new QDrag( m_view ); 00270 drag->setMimeData( mimeData ); 00271 if ( indexes.size() > 1 ) { 00272 drag->setPixmap( KIcon( QLatin1String( "document-multiple" ) ).pixmap( QSize( 22, 22 ) ) ); 00273 } else { 00274 QPixmap pixmap = indexes.first().data( Qt::DecorationRole ).value<QIcon>().pixmap( QSize( 22, 22 ) ); 00275 if ( pixmap.isNull() ) 00276 pixmap = KIcon( QLatin1String( "text-plain" ) ).pixmap( QSize( 22, 22 ) ); 00277 drag->setPixmap( pixmap ); 00278 } 00279 00280 if ( !sourceDeletable ) 00281 supportedActions &= ~Qt::MoveAction; 00282 00283 Qt::DropAction defaultAction = Qt::IgnoreAction; 00284 if ( (QApplication::keyboardModifiers() & Qt::ControlModifier) && 00285 (QApplication::keyboardModifiers() & Qt::ShiftModifier) ) { 00286 defaultAction = Qt::LinkAction; 00287 } else if ( (QApplication::keyboardModifiers() & Qt::ControlModifier) ) { 00288 defaultAction = Qt::CopyAction; 00289 } else if ( (QApplication::keyboardModifiers() & Qt::ShiftModifier) ) { 00290 defaultAction = Qt::MoveAction; 00291 } 00292 00293 drag->exec( supportedActions, defaultAction ); 00294 } 00295 00296 bool DragDropManager::showDropActionMenu() const 00297 { 00298 return mShowDropActionMenu; 00299 } 00300 00301 void DragDropManager::setShowDropActionMenu( bool show ) 00302 { 00303 mShowDropActionMenu = show; 00304 } 00305 00306 bool DragDropManager::isManualSortingActive() const 00307 { 00308 return mIsManualSortingActive; 00309 } 00310 00311 void DragDropManager::setManualSortingActive(bool active) 00312 { 00313 mIsManualSortingActive = active; 00314 } 00315
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Tue May 8 2012 00:00:42 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Tue May 8 2012 00:00:42 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.