2 Copyright (c) 2009 Kevin Ottens <ervin@kde.org>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include <KDE/KActionCollection>
22 #include <KDE/KActionMenu>
24 #include <KDE/KLocale>
26 #include <KDE/KXMLGUIClient>
28 #include <QtCore/QAbstractItemModel>
29 #include <QtGui/QAbstractProxyModel>
30 #include <QtGui/QItemSelectionModel>
31 #include <QtGui/QTabBar>
32 #include <QtGui/QToolButton>
34 #include "storagemodel.h"
36 #include "core/settings.h"
37 #include "core/manager.h"
38 #include "messagecore/messagestatus.h"
39 #include "core/model.h"
47 Private( Pane
*owner
)
48 : q( owner
), mXmlGuiClient( 0 ), mActionMenu( 0 ) { }
50 void onSelectionChanged( const QItemSelection
&selected
, const QItemSelection
&deselected
);
51 void onNewTabClicked();
52 void onCloseTabClicked();
53 void onCurrentTabChanged();
54 void onTabContextMenuRequest( const QPoint
&pos
);
56 QItemSelection
mapSelectionToSource( const QItemSelection
&selection
) const;
57 QItemSelection
mapSelectionFromSource( const QItemSelection
&selection
) const;
58 void updateTabControls();
62 KXMLGUIClient
*mXmlGuiClient
;
63 KActionMenu
*mActionMenu
;
65 QAbstractItemModel
*mModel
;
66 QItemSelectionModel
*mSelectionModel
;
68 QHash
<Widget
*, QItemSelectionModel
*> mWidgetSelectionHash
;
69 QList
<const QAbstractProxyModel
*> mProxyStack
;
71 QToolButton
*mNewTabButton
;
72 QToolButton
*mCloseTabButton
;
76 } // namespace MessageList
78 using namespace Akonadi
;
79 using namespace MessageList
;
82 Pane::Pane( QAbstractItemModel
*model
, QItemSelectionModel
*selectionModel
, QWidget
*parent
)
83 : QTabWidget( parent
), d( new Private( this ) )
86 d
->mSelectionModel
= selectionModel
;
88 // Build the proxy stack
89 const QAbstractProxyModel
*proxyModel
= qobject_cast
<const QAbstractProxyModel
*>( d
->mSelectionModel
->model() );
92 if (static_cast<const QAbstractItemModel
*>(proxyModel
) == d
->mModel
) {
96 d
->mProxyStack
<< proxyModel
;
97 const QAbstractProxyModel
*nextProxyModel
= qobject_cast
<const QAbstractProxyModel
*>(proxyModel
->sourceModel());
99 if (!nextProxyModel
) {
100 // It's the final model in the chain, so it is necessarily the sourceModel.
101 Q_ASSERT(qobject_cast
<const QAbstractItemModel
*>(proxyModel
->sourceModel()) == d
->mModel
);
104 proxyModel
= nextProxyModel
;
105 } // Proxy stack done
107 d
->mNewTabButton
= new QToolButton( this );
108 d
->mNewTabButton
->setIcon( KIcon( "tab-new" ) );
109 d
->mNewTabButton
->adjustSize();
110 d
->mNewTabButton
->setToolTip( i18nc("@info:tooltip", "Open a new tab"));
111 setCornerWidget( d
->mNewTabButton
, Qt::TopLeftCorner
);
112 connect( d
->mNewTabButton
, SIGNAL( clicked() ),
113 SLOT( onNewTabClicked() ) );
115 d
->mCloseTabButton
= new QToolButton( this );
116 d
->mCloseTabButton
->setIcon( KIcon( "tab-close" ) );
117 d
->mCloseTabButton
->adjustSize();
118 d
->mCloseTabButton
->setToolTip( i18nc("@info:tooltip", "Close the current tab"));
119 setCornerWidget( d
->mCloseTabButton
, Qt::TopRightCorner
);
120 connect( d
->mCloseTabButton
, SIGNAL( clicked() ),
121 SLOT( onCloseTabClicked() ) );
126 connect( d
->mSelectionModel
, SIGNAL(selectionChanged(QItemSelection
, QItemSelection
)),
127 this, SLOT(onSelectionChanged(QItemSelection
, QItemSelection
)) );
128 connect( this, SIGNAL(currentChanged(int)),
129 this, SLOT(onCurrentTabChanged()) );
131 setContextMenuPolicy( Qt::CustomContextMenu
);
132 connect( this, SIGNAL(customContextMenuRequested(QPoint
)),
133 this, SLOT(onTabContextMenuRequest(QPoint
)) );
135 connect( Core::Settings::self(), SIGNAL(configChanged()),
136 this, SLOT(updateTabControls()) );
144 void Pane::setXmlGuiClient( KXMLGUIClient
*xmlGuiClient
)
146 d
->mXmlGuiClient
= xmlGuiClient
;
148 for ( int i
=0; i
<count(); i
++ ) {
149 Widget
*w
= qobject_cast
<Widget
*>( widget( i
) );
150 w
->setXmlGuiClient( d
->mXmlGuiClient
);
153 // Setup "View->Message List" actions.
154 if ( xmlGuiClient
) {
155 if ( d
->mActionMenu
) {
156 d
->mXmlGuiClient
->actionCollection()->removeAction( d
->mActionMenu
);
158 d
->mActionMenu
= new KActionMenu( KIcon(), i18n( "Message List" ), this );
159 d
->mXmlGuiClient
->actionCollection()->addAction( "view_message_list", d
->mActionMenu
);
160 const Widget
* const w
= static_cast<Widget
*>( currentWidget() );
161 w
->view()->fillViewMenu( d
->mActionMenu
->menu() );
165 bool Pane::selectNextMessageItem( MessageList::Core::MessageTypeFilter messageTypeFilter
,
166 MessageList::Core::ExistingSelectionBehaviour existingSelectionBehaviour
,
170 Widget
*w
= static_cast<Widget
*>( currentWidget() );
173 if ( w
->view()->model()->isLoading() )
176 return w
->selectNextMessageItem( messageTypeFilter
, existingSelectionBehaviour
, centerItem
, loop
);
182 bool Pane::selectPreviousMessageItem( MessageList::Core::MessageTypeFilter messageTypeFilter
,
183 MessageList::Core::ExistingSelectionBehaviour existingSelectionBehaviour
,
187 Widget
*w
= static_cast<Widget
*>( currentWidget() );
190 if ( w
->view()->model()->isLoading() )
193 return w
->selectPreviousMessageItem( messageTypeFilter
, existingSelectionBehaviour
, centerItem
, loop
);
199 bool Pane::focusNextMessageItem( MessageList::Core::MessageTypeFilter messageTypeFilter
, bool centerItem
, bool loop
)
201 Widget
*w
= static_cast<Widget
*>( currentWidget() );
204 if ( w
->view()->model()->isLoading() )
207 return w
->focusNextMessageItem( messageTypeFilter
, centerItem
, loop
);
213 bool Pane::focusPreviousMessageItem( MessageList::Core::MessageTypeFilter messageTypeFilter
, bool centerItem
, bool loop
)
215 Widget
*w
= static_cast<Widget
*>( currentWidget() );
218 if ( w
->view()->model()->isLoading() )
221 return w
->focusNextMessageItem( messageTypeFilter
, centerItem
, loop
);
227 void Pane::selectFocusedMessageItem( bool centerItem
)
229 Widget
*w
= static_cast<Widget
*>( currentWidget() );
232 if ( w
->view()->model()->isLoading() )
235 w
->selectFocusedMessageItem( centerItem
);
239 bool Pane::selectFirstMessageItem( MessageList::Core::MessageTypeFilter messageTypeFilter
, bool centerItem
)
241 Widget
*w
= static_cast<Widget
*>( currentWidget() );
244 if ( w
->view()->model()->isLoading() )
247 return w
->selectFirstMessageItem( messageTypeFilter
, centerItem
);
253 void Pane::selectAll()
255 Widget
*w
= static_cast<Widget
*>( currentWidget() );
258 if ( w
->view()->model()->isLoading() )
266 void Pane::setCurrentThreadExpanded( bool expand
)
268 Widget
*w
= static_cast<Widget
*>( currentWidget() );
271 if ( w
->view()->model()->isLoading() )
274 w
->setCurrentThreadExpanded(expand
);
278 void Pane::setAllThreadsExpanded( bool expand
)
280 Widget
*w
= static_cast<Widget
*>( currentWidget() );
283 if ( w
->view()->model()->isLoading() )
286 w
->setAllThreadsExpanded( expand
);
290 void Pane::setAllGroupsExpanded( bool expand
)
292 Widget
*w
= static_cast<Widget
*>( currentWidget() );
295 if ( w
->view()->model()->isLoading() )
298 w
->setAllGroupsExpanded(expand
);
302 void Pane::focusQuickSearch()
304 Widget
*w
= static_cast<Widget
*>( currentWidget() );
307 w
->focusQuickSearch();
311 void Pane::Private::onSelectionChanged( const QItemSelection
&selected
, const QItemSelection
&deselected
)
313 Widget
*w
= static_cast<Widget
*>( q
->currentWidget() );
314 QItemSelectionModel
*s
= mWidgetSelectionHash
[w
];
316 s
->select( mapSelectionToSource( selected
), QItemSelectionModel::Select
);
317 s
->select( mapSelectionToSource( deselected
), QItemSelectionModel::Deselect
);
320 QIcon icon
= KIcon( "folder" );
321 foreach ( const QModelIndex
&index
, s
->selectedRows() ) {
322 label
+= index
.data( Qt::DisplayRole
).toString()+", ";
326 if ( label
.isEmpty() ) {
327 label
= i18nc( "@title:tab Empty messagelist", "Empty" );
329 } else if ( s
->selectedRows().size()==1 ) {
330 icon
= s
->selectedRows().first().data( Qt::DecorationRole
).value
<QIcon
>();
333 int index
= q
->indexOf( w
);
334 q
->setTabText( index
, label
);
335 q
->setTabIcon( index
, icon
);
338 void Pane::Private::onNewTabClicked()
344 void Pane::Private::onCloseTabClicked()
346 Widget
*w
= static_cast<Widget
*>( q
->currentWidget() );
347 if ( !w
|| (q
->count() < 2) ) {
355 void Pane::Private::onCurrentTabChanged()
357 Widget
*w
= static_cast<Widget
*>( q
->currentWidget() );
358 QItemSelectionModel
*s
= mWidgetSelectionHash
[w
];
360 disconnect( mSelectionModel
, SIGNAL(selectionChanged(QItemSelection
, QItemSelection
)),
361 q
, SLOT(onSelectionChanged(QItemSelection
, QItemSelection
)) );
363 mSelectionModel
->select( mapSelectionFromSource( s
->selection() ),
364 QItemSelectionModel::ClearAndSelect
);
366 connect( mSelectionModel
, SIGNAL(selectionChanged(QItemSelection
, QItemSelection
)),
367 q
, SLOT(onSelectionChanged(QItemSelection
, QItemSelection
)) );
370 void Pane::Private::onTabContextMenuRequest( const QPoint
&pos
)
372 QTabBar
*bar
= q
->tabBar();
373 int index
= bar
->tabAt( bar
->mapFrom( q
, pos
) );
374 if ( index
== -1 ) return;
376 Widget
*w
= qobject_cast
<Widget
*>( q
->widget( index
) );
382 action
= menu
.addAction( i18nc( "@action:inmenu", "Close Tab" ) );
383 action
->setEnabled( q
->count() > 1 );
384 action
->setIcon( KIcon( "tab-close" ) );
385 connect( action
, SIGNAL(triggered(bool)),
386 q
, SLOT(onCloseTabClicked()) ); // Reuse the logic...
388 QAction
*allOther
= menu
.addAction( i18nc("@action:inmenu", "Close All Other Tabs" ) );
389 allOther
->setEnabled( q
->count() > 1 );
390 allOther
->setIcon( KIcon( "tab-close-other" ) );
392 action
= menu
.exec( q
->mapToGlobal( pos
) );
394 if ( action
== allOther
) { // Close all other tabs
395 QList
<Widget
*> widgets
;
396 int index
= q
->indexOf( w
);
398 for ( int i
=0; i
<q
->count(); i
++ ) {
399 if ( i
==index
) continue; // Skip the current one
401 Widget
*other
= qobject_cast
<Widget
*>( q
->widget( i
) );
405 foreach ( Widget
*other
, widgets
) {
413 void Pane::setCurrentFolder( const Akonadi::Collection
&col
, bool preferEmptyTab
, Core::PreSelectionMode preSelectionMode
, const QString
&overrideLabel
)
415 Widget
*w
= static_cast<Widget
*>( currentWidget() );
418 QItemSelectionModel
*s
= d
->mWidgetSelectionHash
[w
];
419 MessageList::StorageModel
*m
= new MessageList::StorageModel( d
->mModel
, s
, w
);
420 w
->setStorageModel( m
, preSelectionMode
);
421 if ( !overrideLabel
.isEmpty() ) {
422 int index
= indexOf( w
);
423 setTabText( index
, overrideLabel
);
428 void Pane::createNewTab()
430 Widget
* w
= new Widget( this );
431 w
->setXmlGuiClient( d
->mXmlGuiClient
);
432 addTab( w
, i18nc( "@title:tab Empty messagelist", "Empty" ) );
434 QItemSelectionModel
*s
= new QItemSelectionModel( d
->mModel
, w
);
435 MessageList::StorageModel
*m
= new MessageList::StorageModel( d
->mModel
, s
, w
);
436 w
->setStorageModel( m
);
438 d
->mWidgetSelectionHash
[w
] = s
;
440 connect( w
, SIGNAL(messageSelected(Akonadi::Item
)),
441 this, SIGNAL(messageSelected(Akonadi::Item
)) );
442 connect( w
, SIGNAL(messageActivated(Akonadi::Item
)),
443 this, SIGNAL(messageActivated(Akonadi::Item
)) );
444 connect( w
, SIGNAL(selectionChanged()),
445 this, SIGNAL(selectionChanged()) );
446 connect( w
, SIGNAL(messageStatusChangeRequest(Akonadi::Item
, KPIM::MessageStatus
, KPIM::MessageStatus
)),
447 this, SIGNAL(messageStatusChangeRequest(Akonadi::Item
, KPIM::MessageStatus
, KPIM::MessageStatus
)) );
449 connect( w
, SIGNAL(statusMessage(QString
)),
450 this, SIGNAL(statusMessage(QString
)) );
452 connect( w
, SIGNAL( fullSearchRequest() ), this, SIGNAL( fullSearchRequest() ) );
453 d
->updateTabControls();
454 setCurrentWidget( w
);
457 QItemSelection
Pane::Private::mapSelectionToSource( const QItemSelection
&selection
) const
459 QItemSelection result
= selection
;
461 foreach ( const QAbstractProxyModel
*proxy
, mProxyStack
) {
462 result
= proxy
->mapSelectionToSource( result
);
468 QItemSelection
Pane::Private::mapSelectionFromSource( const QItemSelection
&selection
) const
470 QItemSelection result
= selection
;
472 typedef QList
<const QAbstractProxyModel
*>::ConstIterator Iterator
;
474 for ( Iterator it
= mProxyStack
.end()-1; it
!=mProxyStack
.begin(); --it
) {
475 result
= (*it
)->mapSelectionFromSource( result
);
477 result
= mProxyStack
.first()->mapSelectionFromSource( result
);
482 void Pane::Private::updateTabControls()
484 mCloseTabButton
->setEnabled( q
->count()>1 );
486 if ( Core::Settings::self()->autoHideTabBarWithSingleTab() ) {
487 q
->tabBar()->setVisible( q
->count()>1 );
489 q
->tabBar()->setVisible( true );
493 Item
Pane::currentItem() const
495 Widget
*w
= static_cast<Widget
*>( currentWidget() );
501 return w
->currentItem();
504 KMime::Message::Ptr
Pane::currentMessage() const
506 Widget
*w
= static_cast<Widget
*>( currentWidget() );
509 return KMime::Message::Ptr();
512 return w
->currentMessage();
515 QList
<KMime::Message::Ptr
> Pane::selectionAsMessageList( bool includeCollapsedChildren
) const
517 Widget
*w
= static_cast<Widget
*>( currentWidget() );
519 return QList
<KMime::Message::Ptr
>();
521 return w
->selectionAsMessageList( includeCollapsedChildren
);
524 QList
<Akonadi::Item
> Pane::selectionAsMessageItemList( bool includeCollapsedChildren
) const
526 Widget
*w
= static_cast<Widget
*>( currentWidget() );
528 return QList
<Akonadi::Item
>();
530 return w
->selectionAsMessageItemList( includeCollapsedChildren
);
534 QList
<Akonadi::Item
> Pane::currentThreadAsMessageList() const
536 Widget
*w
= static_cast<Widget
*>( currentWidget() );
538 return QList
<Akonadi::Item
>();
540 return w
->currentThreadAsMessageList();
543 QList
<Akonadi::Item
> Pane::itemListFromPersistentSet( MessageList::Core::MessageItemSetReference ref
)
545 Widget
*w
= static_cast<Widget
*>( currentWidget() );
547 return QList
<Akonadi::Item
>();
549 return w
->itemListFromPersistentSet(ref
);
553 void Pane::deletePersistentSet( MessageList::Core::MessageItemSetReference ref
)
555 Widget
*w
= static_cast<Widget
*>( currentWidget() );
557 w
->deletePersistentSet( ref
);
561 void Pane::markMessageItemsAsAboutToBeRemoved( MessageList::Core::MessageItemSetReference ref
, bool bMark
)
563 Widget
*w
= static_cast<Widget
*>( currentWidget() );
565 w
->markMessageItemsAsAboutToBeRemoved( ref
, bMark
);
569 KPIM::MessageStatus
Pane::currentFilterStatus() const
571 Widget
*w
= static_cast<Widget
*>( currentWidget() );
573 return KPIM::MessageStatus();
575 return w
->currentFilterStatus();
578 QString
Pane::currentFilterSearchString() const
580 Widget
*w
= static_cast<Widget
*>( currentWidget() );
584 return w
->currentFilterSearchString();
587 bool Pane::isThreaded() const
589 Widget
*w
= static_cast<Widget
*>( currentWidget() );
593 return w
->isThreaded();
596 bool Pane::selectionEmpty() const
598 Widget
*w
= static_cast<Widget
*>( currentWidget() );
602 return w
->selectionEmpty();
605 bool Pane::getSelectionStats( Akonadi::Item::List
&selectedItems
,
606 Akonadi::Item::List
&selectedVisibleItems
,
607 bool * allSelectedBelongToSameThread
,
608 bool includeCollapsedChildren
) const
610 Widget
* w
= static_cast<Widget
*>( currentWidget() );
615 return w
->getSelectionStats(
616 selectedItems
, selectedVisibleItems
,
617 allSelectedBelongToSameThread
, includeCollapsedChildren
621 MessageList::Core::MessageItemSetReference
Pane::selectionAsPersistentSet( bool includeCollapsedChildren
) const
623 Widget
*w
= static_cast<Widget
*>( currentWidget() );
626 return w
->selectionAsPersistentSet( includeCollapsedChildren
);
629 MessageList::Core::MessageItemSetReference
Pane::currentThreadAsPersistentSet() const
631 Widget
*w
= static_cast<Widget
*>( currentWidget() );
634 return w
->currentThreadAsPersistentSet();
637 void Pane::focusView()
639 Widget
*w
= static_cast<Widget
*>( currentWidget() );
641 QWidget
*view
= w
->view();
647 void Pane::reloadGlobalConfiguration()
649 d
->updateTabControls();
651 Core::Settings::self()->writeConfig();