akonadi
entitytreeview.cpp
00001 /* 00002 Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org> 00003 Copyright (c) 2008 Stephen Kelly <steveire@gmail.com> 00004 00005 This library is free software; you can redistribute it and/or modify it 00006 under the terms of the GNU Library General Public License as published by 00007 the Free Software Foundation; either version 2 of the License, or (at your 00008 option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, but WITHOUT 00011 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00012 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00013 License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to the 00017 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00018 02110-1301, USA. 00019 */ 00020 00021 #include "entitytreeview.h" 00022 00023 #include "dragdropmanager_p.h" 00024 00025 #include <QtCore/QDebug> 00026 #include <QtCore/QTimer> 00027 #include <QtGui/QApplication> 00028 #include <QtGui/QDragMoveEvent> 00029 #include <QtGui/QHeaderView> 00030 #include <QtGui/QMenu> 00031 00032 #include <KAction> 00033 #include <KLocale> 00034 #include <KMessageBox> 00035 #include <KUrl> 00036 #include <KXMLGUIFactory> 00037 00038 #include <akonadi/collection.h> 00039 #include <akonadi/control.h> 00040 #include <akonadi/item.h> 00041 #include <akonadi/entitytreemodel.h> 00042 00043 #include <kdebug.h> 00044 #include <kxmlguiclient.h> 00045 00046 #include "progressspinnerdelegate_p.h" 00047 00048 using namespace Akonadi; 00049 00053 class EntityTreeView::Private 00054 { 00055 public: 00056 Private( EntityTreeView *parent ) 00057 : mParent( parent ) 00058 #ifndef QT_NO_DRAGANDDROP 00059 , mDragDropManager( new DragDropManager( mParent ) ) 00060 #endif 00061 , mXmlGuiClient( 0 ) 00062 { 00063 } 00064 00065 void init(); 00066 void itemClicked( const QModelIndex& ); 00067 void itemDoubleClicked( const QModelIndex& ); 00068 void itemCurrentChanged( const QModelIndex& ); 00069 00070 void slotSelectionChanged( const QItemSelection & selected, const QItemSelection & deselected ); 00071 00072 EntityTreeView *mParent; 00073 QBasicTimer mDragExpandTimer; 00074 DragDropManager *mDragDropManager; 00075 KXMLGUIClient *mXmlGuiClient; 00076 }; 00077 00078 void EntityTreeView::Private::init() 00079 { 00080 Akonadi::DelegateAnimator *animator = new Akonadi::DelegateAnimator(mParent); 00081 Akonadi::ProgressSpinnerDelegate *customDelegate = new Akonadi::ProgressSpinnerDelegate(animator, mParent); 00082 mParent->setItemDelegate(customDelegate); 00083 00084 mParent->header()->setClickable( true ); 00085 mParent->header()->setStretchLastSection( false ); 00086 // mParent->setRootIsDecorated( false ); 00087 00088 // QTreeView::autoExpandDelay has very strange behaviour. It toggles the collapse/expand state 00089 // of the item the cursor is currently over when a timer event fires. 00090 // The behaviour we want is to expand a collapsed row on drag-over, but not collapse it. 00091 // mDragExpandTimer is used to achieve this. 00092 // mParent->setAutoExpandDelay ( QApplication::startDragTime() ); 00093 00094 mParent->setSortingEnabled( true ); 00095 mParent->sortByColumn( 0, Qt::AscendingOrder ); 00096 mParent->setEditTriggers( QAbstractItemView::EditKeyPressed ); 00097 mParent->setAcceptDrops( true ); 00098 #ifndef QT_NO_DRAGANDDROP 00099 mParent->setDropIndicatorShown( true ); 00100 mParent->setDragDropMode( DragDrop ); 00101 mParent->setDragEnabled( true ); 00102 #endif 00103 00104 mParent->connect( mParent, SIGNAL( clicked( const QModelIndex& ) ), 00105 mParent, SLOT( itemClicked( const QModelIndex& ) ) ); 00106 mParent->connect( mParent, SIGNAL( doubleClicked( const QModelIndex& ) ), 00107 mParent, SLOT( itemDoubleClicked( const QModelIndex& ) ) ); 00108 00109 Control::widgetNeedsAkonadi( mParent ); 00110 } 00111 00112 void EntityTreeView::Private::slotSelectionChanged( const QItemSelection & selected, const QItemSelection& ) 00113 { 00114 const int column = 0; 00115 foreach ( const QItemSelectionRange &range, selected ) { 00116 const QModelIndex index = range.topLeft(); 00117 00118 if ( index.column() > 0 ) 00119 continue; 00120 00121 for ( int row = index.row(); row <= range.bottomRight().row(); ++row ) { 00122 // Don't use canFetchMore here. We need to bypass the check in 00123 // the EntityFilterModel when it shows only collections. 00124 mParent->model()->fetchMore( index.sibling( row, column ) ); 00125 } 00126 } 00127 00128 if ( selected.size() == 1 ) { 00129 const QItemSelectionRange &range = selected.first(); 00130 if ( range.topLeft().row() == range.bottomRight().row() ) 00131 mParent->scrollTo( range.topLeft(), QTreeView::EnsureVisible ); 00132 } 00133 } 00134 00135 void EntityTreeView::Private::itemClicked( const QModelIndex &index ) 00136 { 00137 if ( !index.isValid() ) 00138 return; 00139 QModelIndex idx = index.sibling( index.row(), 0); 00140 00141 const Collection collection = idx.model()->data( idx, EntityTreeModel::CollectionRole ).value<Collection>(); 00142 if ( collection.isValid() ) { 00143 emit mParent->clicked( collection ); 00144 } else { 00145 const Item item = idx.model()->data( idx, EntityTreeModel::ItemRole ).value<Item>(); 00146 if ( item.isValid() ) 00147 emit mParent->clicked( item ); 00148 } 00149 } 00150 00151 void EntityTreeView::Private::itemDoubleClicked( const QModelIndex &index ) 00152 { 00153 if ( !index.isValid() ) 00154 return; 00155 QModelIndex idx = index.sibling( index.row(), 0); 00156 const Collection collection = idx.model()->data( idx, EntityTreeModel::CollectionRole ).value<Collection>(); 00157 if ( collection.isValid() ) { 00158 emit mParent->doubleClicked( collection ); 00159 } else { 00160 const Item item = idx.model()->data( idx, EntityTreeModel::ItemRole ).value<Item>(); 00161 if ( item.isValid() ) 00162 emit mParent->doubleClicked( item ); 00163 } 00164 } 00165 00166 void EntityTreeView::Private::itemCurrentChanged( const QModelIndex &index ) 00167 { 00168 if ( !index.isValid() ) 00169 return; 00170 QModelIndex idx = index.sibling( index.row(), 0); 00171 const Collection collection = idx.model()->data( idx, EntityTreeModel::CollectionRole ).value<Collection>(); 00172 if ( collection.isValid() ) { 00173 emit mParent->currentChanged( collection ); 00174 } else { 00175 const Item item = idx.model()->data( idx, EntityTreeModel::ItemRole ).value<Item>(); 00176 if ( item.isValid() ) 00177 emit mParent->currentChanged( item ); 00178 } 00179 } 00180 00181 EntityTreeView::EntityTreeView( QWidget * parent ) 00182 : QTreeView( parent ), 00183 d( new Private( this ) ) 00184 { 00185 setSelectionMode( QAbstractItemView::SingleSelection ); 00186 d->init(); 00187 } 00188 00189 EntityTreeView::EntityTreeView( KXMLGUIClient *xmlGuiClient, QWidget * parent ) 00190 : QTreeView( parent ), 00191 d( new Private( this ) ) 00192 { 00193 d->mXmlGuiClient = xmlGuiClient; 00194 d->init(); 00195 } 00196 00197 EntityTreeView::~EntityTreeView() 00198 { 00199 delete d->mDragDropManager; 00200 delete d; 00201 } 00202 00203 void EntityTreeView::setModel( QAbstractItemModel * model ) 00204 { 00205 if ( selectionModel() ) { 00206 disconnect( selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ), 00207 this, SLOT( itemCurrentChanged( const QModelIndex& ) ) ); 00208 00209 disconnect( selectionModel(), SIGNAL( selectionChanged( const QItemSelection&, const QItemSelection& ) ), 00210 this, SLOT( slotSelectionChanged( const QItemSelection&, const QItemSelection& ) ) ); 00211 } 00212 00213 QTreeView::setModel( model ); 00214 header()->setStretchLastSection( true ); 00215 00216 connect( selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ), 00217 SLOT( itemCurrentChanged( const QModelIndex& ) ) ); 00218 00219 connect( selectionModel(), SIGNAL( selectionChanged( const QItemSelection&, const QItemSelection& ) ), 00220 SLOT( slotSelectionChanged( const QItemSelection&, const QItemSelection& ) ) ); 00221 } 00222 00223 00224 void EntityTreeView::timerEvent( QTimerEvent *event ) 00225 { 00226 if ( event->timerId() == d->mDragExpandTimer.timerId() ) { 00227 const QPoint pos = viewport()->mapFromGlobal( QCursor::pos() ); 00228 if ( state() == QAbstractItemView::DraggingState && viewport()->rect().contains( pos ) ) 00229 setExpanded( indexAt( pos ), true ); 00230 } 00231 00232 QTreeView::timerEvent( event ); 00233 } 00234 00235 #ifndef QT_NO_DRAGANDDROP 00236 void EntityTreeView::dragMoveEvent( QDragMoveEvent * event ) 00237 { 00238 d->mDragExpandTimer.start( QApplication::startDragTime() , this ); 00239 00240 if ( d->mDragDropManager->dropAllowed( event ) ) { 00241 // All urls are supported. process the event. 00242 QTreeView::dragMoveEvent( event ); 00243 return; 00244 } 00245 00246 event->setDropAction( Qt::IgnoreAction ); 00247 return; 00248 } 00249 00250 void EntityTreeView::dropEvent( QDropEvent * event ) 00251 { 00252 if ( d->mDragDropManager->processDropEvent( event ) ) 00253 QTreeView::dropEvent( event ); 00254 00255 d->mDragExpandTimer.stop(); 00256 } 00257 #endif 00258 00259 #ifndef QT_NO_CONTEXTMENU 00260 void EntityTreeView::contextMenuEvent( QContextMenuEvent * event ) 00261 { 00262 if ( !d->mXmlGuiClient || !model()) 00263 return; 00264 00265 const QModelIndex index = indexAt( event->pos() ); 00266 00267 QMenu *popup = 0; 00268 00269 // check if the index under the cursor is a collection or item 00270 const Item item = model()->data( index, EntityTreeModel::ItemRole ).value<Item>(); 00271 if ( item.isValid() ) 00272 popup = static_cast<QMenu*>( d->mXmlGuiClient->factory()->container( 00273 QLatin1String( "akonadi_itemview_contextmenu" ), d->mXmlGuiClient ) ); 00274 else 00275 popup = static_cast<QMenu*>( d->mXmlGuiClient->factory()->container( 00276 QLatin1String( "akonadi_collectionview_contextmenu" ), d->mXmlGuiClient ) ); 00277 if ( popup ) 00278 popup->exec( event->globalPos() ); 00279 } 00280 #endif 00281 00282 void EntityTreeView::setXmlGuiClient( KXMLGUIClient * xmlGuiClient ) 00283 { 00284 d->mXmlGuiClient = xmlGuiClient; 00285 } 00286 00287 #ifndef QT_NO_DRAGANDDROP 00288 void EntityTreeView::startDrag( Qt::DropActions supportedActions ) 00289 { 00290 d->mDragDropManager->startDrag( supportedActions ); 00291 } 00292 #endif 00293 00294 00295 void EntityTreeView::setDropActionMenuEnabled( bool enabled ) 00296 { 00297 #ifndef QT_NO_DRAGANDDROP 00298 d->mDragDropManager->setShowDropActionMenu( enabled ); 00299 #endif 00300 } 00301 00302 bool EntityTreeView::isDropActionMenuEnabled() const 00303 { 00304 #ifndef QT_NO_DRAGANDDROP 00305 return d->mDragDropManager->showDropActionMenu(); 00306 #else 00307 return false; 00308 #endif 00309 } 00310 00311 #include "entitytreeview.moc"