Build with clang.
[kdepim.git] / mailcommon / foldertreeview.cpp
blobba2abd02242a7e3475ebf59e0cc84996366661a3
1 /* -*- mode: C++; c-file-style: "gnu" -*-
2 This file is part of KMail, the KDE mail client.
3 Copyright (c) 2009 Montel Laurent <montel@kde.org>
5 KMail is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License, version 2, as
7 published by the Free Software Foundation.
9 KMail is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 #include "foldertreeview.h"
20 #include "mailkernel.h"
22 #include <KDebug>
23 #include <KLocale>
24 #include <akonadi/entitytreemodel.h>
25 #include <akonadi/collectionstatistics.h>
26 #include <akonadi/collectionstatisticsdelegate.h>
27 #include <KMessageBox>
28 #include <KGuiItem>
29 #include <KMenu>
30 #include <KConfigGroup>
32 #include <QHeaderView>
33 #include <QMouseEvent>
35 namespace MailCommon {
37 FolderTreeView::FolderTreeView( QWidget* parent, bool showUnreadCount )
38 : Akonadi::EntityTreeView( parent ),
39 mbDisableContextMenuAndExtraColumn( false )
41 init(showUnreadCount);
45 FolderTreeView::FolderTreeView( KXMLGUIClient* xmlGuiClient, QWidget* parent, bool showUnreadCount )
46 :Akonadi::EntityTreeView( xmlGuiClient, parent ), mbDisableContextMenuAndExtraColumn( false )
48 init(showUnreadCount);
52 FolderTreeView::~FolderTreeView()
57 void FolderTreeView::setTooltipsPolicy( FolderTreeWidget::ToolTipDisplayPolicy policy )
59 mToolTipDisplayPolicy = policy;
60 writeConfig();
63 void FolderTreeView::disableContextMenuAndExtraColumn()
65 mbDisableContextMenuAndExtraColumn = true;
66 const int nbColumn = header()->count();
67 for ( int i = 1; i <nbColumn; ++i )
69 setColumnHidden( i, true );
73 void FolderTreeView::init( bool showUnreadCount )
75 setIconSize( QSize( 22, 22 ) );
76 mSortingPolicy = FolderTreeWidget::SortByCurrentColumn;
77 mToolTipDisplayPolicy = FolderTreeWidget::DisplayAlways;
79 header()->setContextMenuPolicy( Qt::CustomContextMenu );
80 connect( header(), SIGNAL(customContextMenuRequested(QPoint)),
81 SLOT(slotHeaderContextMenuRequested(QPoint)) );
82 readConfig();
84 mCollectionStatisticsDelegate = new Akonadi::CollectionStatisticsDelegate( this );
85 mCollectionStatisticsDelegate->setProgressAnimationEnabled( true );
86 setItemDelegate(mCollectionStatisticsDelegate);
87 mCollectionStatisticsDelegate->setUnreadCountShown( showUnreadCount && !header()->isSectionHidden( 1 ) );
91 void FolderTreeView::showStatisticAnimation( bool anim )
93 mCollectionStatisticsDelegate->setProgressAnimationEnabled( anim );
96 void FolderTreeView::writeConfig()
98 KConfigGroup myGroup( KernelIf->config(), "MainFolderView");
99 myGroup.writeEntry( "IconSize", iconSize().width() );
100 myGroup.writeEntry( "ToolTipDisplayPolicy", ( int ) mToolTipDisplayPolicy );
101 myGroup.writeEntry( "SortingPolicy", ( int ) mSortingPolicy );
104 void FolderTreeView::readConfig()
106 KConfigGroup myGroup( KernelIf->config(), "MainFolderView" );
107 int iIconSize = myGroup.readEntry( "IconSize", iconSize().width() );
108 if ( iIconSize < 16 || iIconSize > 32 )
109 iIconSize = 22;
110 setIconSize( QSize( iIconSize, iIconSize ) );
111 setTooltipsPolicy( static_cast<FolderTreeWidget::ToolTipDisplayPolicy>( myGroup.readEntry( "ToolTipDisplayPolicy", static_cast<int>( FolderTreeWidget::DisplayAlways ) ) ) );
112 setSortingPolicy( ( FolderTreeWidget::SortingPolicy )myGroup.readEntry( "SortingPolicy", ( int ) mSortingPolicy ) );
115 void FolderTreeView::slotHeaderContextMenuRequested( const QPoint&pnt )
117 if ( mbDisableContextMenuAndExtraColumn ) {
118 readConfig();
119 return;
122 // the menu for the columns
123 KMenu menu;
124 QAction *act;
125 menu.addTitle( i18n("View Columns") );
126 const int nbColumn = header()->count();
127 for ( int i = 1; i <nbColumn; ++i )
129 act = menu.addAction( model()->headerData( i, Qt::Horizontal ).toString() );
130 act->setCheckable( true );
131 act->setChecked( !header()->isSectionHidden( i ) );
132 act->setData( QVariant( i ) );
133 if ( i == 0)
134 act->setEnabled( false );
135 connect( act, SIGNAL(triggered(bool)),
136 SLOT(slotHeaderContextMenuChangeHeader(bool)) );
140 menu.addTitle( i18n( "Icon Size" ) );
142 static int icon_sizes[] = { 16, 22, 32 /*, 48, 64, 128 */ };
144 QActionGroup *grp = new QActionGroup( &menu );
146 for ( int i = 0; i < (int)( sizeof( icon_sizes ) / sizeof( int ) ); i++ )
148 act = menu.addAction( QString("%1x%2").arg( icon_sizes[ i ] ).arg( icon_sizes[ i ] ) );
149 act->setCheckable( true );
150 grp->addAction( act );
151 if ( iconSize().width() == icon_sizes[ i ] )
152 act->setChecked( true );
153 act->setData( QVariant( icon_sizes[ i ] ) );
155 connect( act, SIGNAL(triggered(bool)),
156 SLOT(slotHeaderContextMenuChangeIconSize(bool)) );
158 menu.addTitle( i18n( "Display Tooltips" ) );
160 grp = new QActionGroup( &menu );
162 act = menu.addAction( i18nc("@action:inmenu Always display tooltips", "Always") );
163 act->setCheckable( true );
164 grp->addAction( act );
165 act->setChecked( mToolTipDisplayPolicy == FolderTreeWidget::DisplayAlways );
166 act->setData( QVariant( (int)FolderTreeWidget::DisplayAlways ) );
167 connect( act, SIGNAL(triggered(bool)),
168 SLOT(slotHeaderContextMenuChangeToolTipDisplayPolicy(bool)) );
169 act = menu.addAction( i18nc("@action:inmenu", "When Text Obscured") );
170 act->setCheckable( true );
172 //Port it !!!!
173 act->setEnabled( false );
174 grp->addAction( act );
175 act->setChecked( mToolTipDisplayPolicy == FolderTreeWidget::DisplayWhenTextElided );
176 act->setData( QVariant( (int)FolderTreeWidget::DisplayWhenTextElided ) );
177 connect( act, SIGNAL(triggered(bool)),
178 SLOT(slotHeaderContextMenuChangeToolTipDisplayPolicy(bool)) );
180 act = menu.addAction( i18nc("@action:inmenu Never display tooltips.", "Never") );
181 act->setCheckable( true );
182 grp->addAction( act );
183 act->setChecked( mToolTipDisplayPolicy == FolderTreeWidget::DisplayNever );
184 act->setData( QVariant( (int)FolderTreeWidget::DisplayNever ) );
185 connect( act, SIGNAL(triggered(bool)),
186 SLOT(slotHeaderContextMenuChangeToolTipDisplayPolicy(bool)) );
188 menu.addTitle( i18nc("@action:inmenu", "Sort Items" ) );
190 grp = new QActionGroup( &menu );
192 act = menu.addAction( i18nc("@action:inmenu", "Automatically, by Current Column") );
193 act->setCheckable( true );
194 grp->addAction( act );
195 act->setChecked( mSortingPolicy == FolderTreeWidget::SortByCurrentColumn );
196 act->setData( QVariant( (int)FolderTreeWidget::SortByCurrentColumn ) );
197 connect( act, SIGNAL(triggered(bool)),
198 SLOT(slotHeaderContextMenuChangeSortingPolicy(bool)) );
200 act = menu.addAction( i18nc("@action:inmenu", "Manually, by Drag And Drop") );
201 act->setCheckable( true );
202 grp->addAction( act );
203 act->setChecked( mSortingPolicy == FolderTreeWidget::SortByDragAndDropKey );
204 act->setData( QVariant( (int)FolderTreeWidget::SortByDragAndDropKey ) );
205 connect( act, SIGNAL(triggered(bool)),
206 SLOT(slotHeaderContextMenuChangeSortingPolicy(bool)) );
208 menu.exec( header()->mapToGlobal( pnt ) );
211 void FolderTreeView::slotHeaderContextMenuChangeSortingPolicy( bool )
213 QAction *act = dynamic_cast< QAction * >( sender() );
214 if ( !act )
215 return;
217 QVariant data = act->data();
219 bool ok;
220 int policy = data.toInt( &ok );
221 if ( !ok )
222 return;
224 setSortingPolicy( ( FolderTreeWidget::SortingPolicy )policy );
228 void FolderTreeView::setSortingPolicy( FolderTreeWidget::SortingPolicy policy )
230 mSortingPolicy = policy;
231 switch ( mSortingPolicy )
233 case FolderTreeWidget::SortByCurrentColumn:
234 header()->setClickable( true );
235 header()->setSortIndicatorShown( true );
236 setSortingEnabled( true );
237 emit manualSortingChanged( false );
238 break;
239 case FolderTreeWidget::SortByDragAndDropKey:
240 header()->setClickable( false );
241 header()->setSortIndicatorShown( false );
242 #if 0
244 // Qt 4.5 introduced a nasty bug here:
245 // Sorting must be enabled in order to sortByColumn() to work.
246 // If sorting is disabled it disconnects some internal signal/slot pairs
247 // and calling sortByColumn() silently has no effect.
248 // This is a bug as we actually DON'T want automatic sorting to be
249 // performed by the view whenever it wants. We want to control sorting.
251 setSortingEnabled( true ); // hack for qutie bug: the param here should be false
252 sortByColumn( 0, Qt::AscendingOrder );
253 #endif
254 setSortingEnabled( false ); // hack for qutie bug: this call shouldn't be here at all
255 emit manualSortingChanged( true );
257 break;
258 default:
259 // should never happen
260 break;
262 writeConfig();
265 void FolderTreeView::slotHeaderContextMenuChangeToolTipDisplayPolicy( bool )
267 QAction *act = dynamic_cast< QAction * >( sender() );
268 if ( !act )
269 return;
271 QVariant data = act->data();
273 bool ok;
274 const int id = data.toInt( &ok );
275 if ( !ok )
276 return;
277 emit changeTooltipsPolicy( ( FolderTreeWidget::ToolTipDisplayPolicy )id );
280 void FolderTreeView::slotHeaderContextMenuChangeHeader( bool )
282 QAction *act = dynamic_cast< QAction * >( sender() );
283 if ( !act )
284 return;
286 QVariant data = act->data();
288 bool ok;
289 const int id = data.toInt( &ok );
290 if ( !ok )
291 return;
293 if ( id > header()->count() )
294 return;
296 if ( id == 1 )
297 mCollectionStatisticsDelegate->setUnreadCountShown(!act->isChecked());
299 setColumnHidden( id, !act->isChecked() );
302 void FolderTreeView::slotHeaderContextMenuChangeIconSize( bool )
304 QAction *act = dynamic_cast< QAction * >( sender() );
305 if ( !act )
306 return;
308 QVariant data = act->data();
310 bool ok;
311 const int size = data.toInt( &ok );
312 if ( !ok )
313 return;
315 setIconSize( QSize( size, size ) );
316 writeConfig();
319 void FolderTreeView::setCurrentModelIndex( const QModelIndex & index )
321 if ( index.isValid() ) {
322 clearSelection();
323 scrollTo( index );
324 selectionModel()->setCurrentIndex( index, QItemSelectionModel::Rows );
328 void FolderTreeView::selectModelIndex( const QModelIndex & index )
330 if ( index.isValid() ) {
331 scrollTo( index );
332 selectionModel()->select( index, QItemSelectionModel::Rows | QItemSelectionModel::Select |
333 QItemSelectionModel::Current | QItemSelectionModel::Clear );
337 void FolderTreeView::slotSelectFocusFolder()
339 const QModelIndex index = currentIndex();
340 if( index.isValid() )
341 setCurrentIndex( index );
344 void FolderTreeView::slotFocusNextFolder()
346 const QModelIndex nextFolder = selectNextFolder( currentIndex() );
348 if ( nextFolder.isValid() ) {
349 expand( nextFolder );
350 setCurrentModelIndex( nextFolder );
354 QModelIndex FolderTreeView::selectNextFolder( const QModelIndex & current )
356 QModelIndex below;
357 if ( current.isValid() ) {
358 model()->fetchMore( current );
359 if ( model()->hasChildren( current ) ) {
360 expand( current );
361 below = indexBelow( current );
362 } else if ( current.row() < model()->rowCount( model()->parent( current ) ) -1 ) {
363 below = model()->index( current.row()+1, current.column(), model()->parent( current ) );
364 } else {
365 below = indexBelow( current );
368 return below;
371 void FolderTreeView::slotFocusPrevFolder()
373 const QModelIndex current = currentIndex();
374 if ( current.isValid() ) {
375 QModelIndex above = indexAbove( current );
376 setCurrentModelIndex( above );
380 void FolderTreeView::selectNextUnreadFolder( bool confirm )
382 // find next unread collection starting from current position
383 if ( !trySelectNextUnreadFolder( currentIndex(), MailCommon::Util::ForwardSearch, confirm ) ) {
384 // if there is none, jump to the last collection and try again
385 trySelectNextUnreadFolder( model()->index( 0, 0 ), MailCommon::Util::ForwardSearch, confirm );
389 // helper method to find last item in the model tree
390 static QModelIndex lastChildOf( QAbstractItemModel *model, const QModelIndex &current )
392 if ( model->rowCount( current ) == 0 )
393 return current;
395 return lastChildOf( model, model->index( model->rowCount( current ) - 1, 0, current ) );
398 void FolderTreeView::selectPrevUnreadFolder( bool confirm )
400 // find next unread collection starting from current position
401 if ( !trySelectNextUnreadFolder( currentIndex(), MailCommon::Util::BackwardSearch, confirm ) ) {
402 // if there is none, jump to top and try again
403 const QModelIndex index = lastChildOf( model(), QModelIndex() );
404 trySelectNextUnreadFolder( index, MailCommon::Util::BackwardSearch, confirm );
408 bool FolderTreeView::trySelectNextUnreadFolder( const QModelIndex &current, MailCommon::Util::SearchDirection direction, bool confirm )
410 QModelIndex index = current;
411 while ( true ) {
412 index = MailCommon::Util::nextUnreadCollection( model(), index, direction );
414 if ( !index.isValid() )
415 return false;
417 const Akonadi::Collection collection = index.data( Akonadi::EntityTreeModel::CollectionRole ).value<Akonadi::Collection>();
418 if ( ignoreUnreadFolder( collection, confirm ) )
419 continue;
421 if ( allowedToEnterFolder( collection, confirm ) ) {
422 expand( index );
423 setCurrentIndex( index );
424 selectModelIndex( index );
425 return true;
426 } else {
427 return false;
431 return false;
434 bool FolderTreeView::ignoreUnreadFolder( const Akonadi::Collection &collection, bool confirm ) const
436 if ( !confirm )
437 return false;
439 // Skip drafts, sent mail and templates as well, when reading mail with the
440 // space bar - but not when changing into the next folder with unread mail
441 // via ctrl+ or ctrl- so we do this only if (confirm == true), which means
442 // we are doing readOn.
444 return ( collection == Kernel::self()->draftsCollectionFolder() ||
445 collection == Kernel::self()->templatesCollectionFolder() ||
446 collection == Kernel::self()->sentCollectionFolder() );
449 bool FolderTreeView::allowedToEnterFolder( const Akonadi::Collection &collection, bool confirm ) const
451 if ( !confirm )
452 return true;
454 // warn user that going to next folder - but keep track of
455 // whether he wishes to be notified again in "AskNextFolder"
456 // parameter (kept in the config file for kmail)
457 const int result = KMessageBox::questionYesNo( const_cast<FolderTreeView*>( this ),
458 i18n( "<qt>Go to the next unread message in folder <b>%1</b>?</qt>" , collection.name() ),
459 i18n( "Go to Next Unread Message" ),
460 KGuiItem( i18n( "Go To" ) ),
461 KGuiItem( i18n( "Do Not Go To" ) ), // defaults
462 ":kmail_AskNextFolder", 0 );
463 return (result == KMessageBox::Yes);
466 bool FolderTreeView::isUnreadFolder( const QModelIndex &current, QModelIndex &index, FolderTreeView::Move move, bool confirm )
468 if ( current.isValid() ) {
470 if ( move == FolderTreeView::Next )
471 index = selectNextFolder( current );
472 else if ( move == FolderTreeView::Previous )
473 index = indexAbove( current );
475 if ( index.isValid() ) {
476 const Akonadi::Collection collection = index.model()->data( current, Akonadi::EntityTreeModel::CollectionRole ).value<Akonadi::Collection>();
477 if ( collection.isValid() ) {
478 if ( collection.statistics().unreadCount() > 0 ) {
479 if ( !confirm ) {
480 selectModelIndex( current );
481 return true;
482 } else {
483 // Skip drafts, sent mail and templates as well, when reading mail with the
484 // space bar - but not when changing into the next folder with unread mail
485 // via ctrl+ or ctrl- so we do this only if (confirm == true), which means
486 // we are doing readOn.
488 if ( collection == Kernel::self()->draftsCollectionFolder() ||
489 collection == Kernel::self()->templatesCollectionFolder() ||
490 collection == Kernel::self()->sentCollectionFolder() )
491 return false;
493 // warn user that going to next folder - but keep track of
494 // whether he wishes to be notified again in "AskNextFolder"
495 // parameter (kept in the config file for kmail)
496 if (KMessageBox::questionYesNo(
497 this,
498 i18n( "<qt>Go to the next unread message in folder <b>%1</b>?</qt>" , collection.name() ),
499 i18n( "Go to Next Unread Message" ),
500 KGuiItem( i18n( "Go To" ) ),
501 KGuiItem( i18n( "Do Not Go To" ) ), // defaults
502 ":kmail_AskNextFolder",
504 ) == KMessageBox::No
506 return true; // assume selected (do not continue looping)
508 selectModelIndex( current );
509 return true;
515 return false;
518 Akonadi::Collection FolderTreeView::currentFolder() const
520 const QModelIndex current = currentIndex();
521 if ( current.isValid() ) {
522 const Akonadi::Collection collection = current.model()->data( current, Akonadi::EntityTreeModel::CollectionRole ).value<Akonadi::Collection>();
523 return collection;
525 return Akonadi::Collection();
528 void FolderTreeView::mousePressEvent( QMouseEvent * e )
530 const bool buttonPressedIsMiddle = ( e->button() == Qt::MidButton );
531 emit prefereCreateNewTab( buttonPressedIsMiddle );
532 EntityTreeView::mousePressEvent( e );
535 void FolderTreeView::restoreHeaderState( const QByteArray& data )
537 header()->restoreState( data );
538 mCollectionStatisticsDelegate->setUnreadCountShown( header()->isSectionHidden( 1 ) );
544 #include "foldertreeview.moc"