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 00129 void EntityTreeView::Private::itemClicked( const QModelIndex &index ) 00130 { 00131 if ( !index.isValid() ) 00132 return; 00133 QModelIndex idx = index.sibling( index.row(), 0); 00134 00135 const Collection collection = idx.model()->data( idx, EntityTreeModel::CollectionRole ).value<Collection>(); 00136 if ( collection.isValid() ) { 00137 emit mParent->clicked( collection ); 00138 } else { 00139 const Item item = idx.model()->data( idx, EntityTreeModel::ItemRole ).value<Item>(); 00140 if ( item.isValid() ) 00141 emit mParent->clicked( item ); 00142 } 00143 } 00144 00145 void EntityTreeView::Private::itemDoubleClicked( const QModelIndex &index ) 00146 { 00147 if ( !index.isValid() ) 00148 return; 00149 QModelIndex idx = index.sibling( index.row(), 0); 00150 const Collection collection = idx.model()->data( idx, EntityTreeModel::CollectionRole ).value<Collection>(); 00151 if ( collection.isValid() ) { 00152 emit mParent->doubleClicked( collection ); 00153 } else { 00154 const Item item = idx.model()->data( idx, EntityTreeModel::ItemRole ).value<Item>(); 00155 if ( item.isValid() ) 00156 emit mParent->doubleClicked( item ); 00157 } 00158 } 00159 00160 void EntityTreeView::Private::itemCurrentChanged( const QModelIndex &index ) 00161 { 00162 if ( !index.isValid() ) 00163 return; 00164 QModelIndex idx = index.sibling( index.row(), 0); 00165 const Collection collection = idx.model()->data( idx, EntityTreeModel::CollectionRole ).value<Collection>(); 00166 if ( collection.isValid() ) { 00167 emit mParent->currentChanged( collection ); 00168 } else { 00169 const Item item = idx.model()->data( idx, EntityTreeModel::ItemRole ).value<Item>(); 00170 if ( item.isValid() ) 00171 emit mParent->currentChanged( item ); 00172 } 00173 } 00174 00175 EntityTreeView::EntityTreeView( QWidget * parent ) 00176 : QTreeView( parent ), 00177 d( new Private( this ) ) 00178 { 00179 setSelectionMode( QAbstractItemView::SingleSelection ); 00180 d->init(); 00181 } 00182 00183 EntityTreeView::EntityTreeView( KXMLGUIClient *xmlGuiClient, QWidget * parent ) 00184 : QTreeView( parent ), 00185 d( new Private( this ) ) 00186 { 00187 d->mXmlGuiClient = xmlGuiClient; 00188 d->init(); 00189 } 00190 00191 EntityTreeView::~EntityTreeView() 00192 { 00193 delete d->mDragDropManager; 00194 delete d; 00195 } 00196 00197 void EntityTreeView::setModel( QAbstractItemModel * model ) 00198 { 00199 if ( selectionModel() ) { 00200 disconnect( selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ), 00201 this, SLOT( itemCurrentChanged( const QModelIndex& ) ) ); 00202 00203 disconnect( selectionModel(), SIGNAL( selectionChanged( const QItemSelection&, const QItemSelection& ) ), 00204 this, SLOT( slotSelectionChanged( const QItemSelection&, const QItemSelection& ) ) ); 00205 } 00206 00207 QTreeView::setModel( model ); 00208 header()->setStretchLastSection( true ); 00209 00210 connect( selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ), 00211 SLOT( itemCurrentChanged( const QModelIndex& ) ) ); 00212 00213 connect( selectionModel(), SIGNAL( selectionChanged( const QItemSelection&, const QItemSelection& ) ), 00214 SLOT( slotSelectionChanged( const QItemSelection&, const QItemSelection& ) ) ); 00215 } 00216 00217 00218 void EntityTreeView::timerEvent( QTimerEvent *event ) 00219 { 00220 if ( event->timerId() == d->mDragExpandTimer.timerId() ) { 00221 const QPoint pos = viewport()->mapFromGlobal( QCursor::pos() ); 00222 if ( state() == QAbstractItemView::DraggingState && viewport()->rect().contains( pos ) ) 00223 setExpanded( indexAt( pos ), true ); 00224 } 00225 00226 QTreeView::timerEvent( event ); 00227 } 00228 00229 #ifndef QT_NO_DRAGANDDROP 00230 void EntityTreeView::dragMoveEvent( QDragMoveEvent * event ) 00231 { 00232 d->mDragExpandTimer.start( QApplication::startDragTime() , this ); 00233 00234 if ( d->mDragDropManager->dropAllowed( event ) ) { 00235 // All urls are supported. process the event. 00236 QTreeView::dragMoveEvent( event ); 00237 return; 00238 } 00239 00240 event->setDropAction( Qt::IgnoreAction ); 00241 return; 00242 } 00243 00244 void EntityTreeView::dropEvent( QDropEvent * event ) 00245 { 00246 if ( d->mDragDropManager->processDropEvent( event ) ) 00247 QTreeView::dropEvent( event ); 00248 00249 d->mDragExpandTimer.stop(); 00250 } 00251 #endif 00252 00253 #ifndef QT_NO_CONTEXTMENU 00254 void EntityTreeView::contextMenuEvent( QContextMenuEvent * event ) 00255 { 00256 if ( !d->mXmlGuiClient || !model()) 00257 return; 00258 00259 const QModelIndex index = indexAt( event->pos() ); 00260 00261 QMenu *popup = 0; 00262 00263 // check if the index under the cursor is a collection or item 00264 const Item item = model()->data( index, EntityTreeModel::ItemRole ).value<Item>(); 00265 if ( item.isValid() ) 00266 popup = static_cast<QMenu*>( d->mXmlGuiClient->factory()->container( 00267 QLatin1String( "akonadi_itemview_contextmenu" ), d->mXmlGuiClient ) ); 00268 else 00269 popup = static_cast<QMenu*>( d->mXmlGuiClient->factory()->container( 00270 QLatin1String( "akonadi_collectionview_contextmenu" ), d->mXmlGuiClient ) ); 00271 if ( popup ) 00272 popup->exec( event->globalPos() ); 00273 } 00274 #endif 00275 00276 void EntityTreeView::setXmlGuiClient( KXMLGUIClient * xmlGuiClient ) 00277 { 00278 d->mXmlGuiClient = xmlGuiClient; 00279 } 00280 00281 #ifndef QT_NO_DRAGANDDROP 00282 void EntityTreeView::startDrag( Qt::DropActions supportedActions ) 00283 { 00284 d->mDragDropManager->startDrag( supportedActions ); 00285 } 00286 #endif 00287 00288 00289 void EntityTreeView::setDropActionMenuEnabled( bool enabled ) 00290 { 00291 #ifndef QT_NO_DRAGANDDROP 00292 d->mDragDropManager->setShowDropActionMenu( enabled ); 00293 #endif 00294 } 00295 00296 bool EntityTreeView::isDropActionMenuEnabled() const 00297 { 00298 #ifndef QT_NO_DRAGANDDROP 00299 return d->mDragDropManager->showDropActionMenu(); 00300 #else 00301 return false; 00302 #endif 00303 } 00304 00305 #include "entitytreeview.moc"