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(QModelIndex)), 00105 mParent, SLOT(itemClicked(QModelIndex)) ); 00106 mParent->connect( mParent, SIGNAL(doubleClicked(QModelIndex)), 00107 mParent, SLOT(itemDoubleClicked(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(QModelIndex,QModelIndex)), 00207 this, SLOT(itemCurrentChanged(QModelIndex)) ); 00208 00209 disconnect( selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), 00210 this, SLOT(slotSelectionChanged(QItemSelection,QItemSelection)) ); 00211 } 00212 00213 QTreeView::setModel( model ); 00214 header()->setStretchLastSection( true ); 00215 00216 connect( selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), 00217 SLOT(itemCurrentChanged(QModelIndex)) ); 00218 00219 connect( selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), 00220 SLOT(slotSelectionChanged(QItemSelection,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 } 00248 00249 void EntityTreeView::dropEvent( QDropEvent * event ) 00250 { 00251 d->mDragExpandTimer.stop(); 00252 bool menuCanceled = false; 00253 if ( d->mDragDropManager->processDropEvent( event, menuCanceled, ( dropIndicatorPosition () == QAbstractItemView::OnItem ) ) ) 00254 QTreeView::dropEvent( event ); 00255 } 00256 #endif 00257 00258 #ifndef QT_NO_CONTEXTMENU 00259 void EntityTreeView::contextMenuEvent( QContextMenuEvent * event ) 00260 { 00261 if ( !d->mXmlGuiClient || !model()) 00262 return; 00263 00264 const QModelIndex index = indexAt( event->pos() ); 00265 00266 QMenu *popup = 0; 00267 00268 // check if the index under the cursor is a collection or item 00269 const Item item = model()->data( index, EntityTreeModel::ItemRole ).value<Item>(); 00270 if ( item.isValid() ) 00271 popup = static_cast<QMenu*>( d->mXmlGuiClient->factory()->container( 00272 QLatin1String( "akonadi_itemview_contextmenu" ), d->mXmlGuiClient ) ); 00273 else 00274 popup = static_cast<QMenu*>( d->mXmlGuiClient->factory()->container( 00275 QLatin1String( "akonadi_collectionview_contextmenu" ), d->mXmlGuiClient ) ); 00276 if ( popup ) 00277 popup->exec( event->globalPos() ); 00278 } 00279 #endif 00280 00281 void EntityTreeView::setXmlGuiClient( KXMLGUIClient * xmlGuiClient ) 00282 { 00283 d->mXmlGuiClient = xmlGuiClient; 00284 } 00285 00286 #ifndef QT_NO_DRAGANDDROP 00287 void EntityTreeView::startDrag( Qt::DropActions supportedActions ) 00288 { 00289 d->mDragDropManager->startDrag( supportedActions ); 00290 } 00291 #endif 00292 00293 00294 void EntityTreeView::setDropActionMenuEnabled( bool enabled ) 00295 { 00296 #ifndef QT_NO_DRAGANDDROP 00297 d->mDragDropManager->setShowDropActionMenu( enabled ); 00298 #endif 00299 } 00300 00301 bool EntityTreeView::isDropActionMenuEnabled() const 00302 { 00303 #ifndef QT_NO_DRAGANDDROP 00304 return d->mDragDropManager->showDropActionMenu(); 00305 #else 00306 return false; 00307 #endif 00308 } 00309 00310 #include "entitytreeview.moc"