You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1168 lines
32 KiB

/******************************************************************************
*
* Copyright 2008 Szymon Tomasz Stefanek <pragma@kvirc.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*******************************************************************************/
#include "messagelistview/core/widgetbase.h"
#include "messagelistview/core/aggregation.h"
#include "messagelistview/core/theme.h"
#include "messagelistview/core/filter.h"
#include "messagelistview/core/manager.h"
#include "messagelistview/core/optionset.h"
#include "messagelistview/core/view.h"
#include "messagelistview/core/model.h"
#include "messagelistview/core/messageitem.h"
#include "messagelistview/core/storagemodelbase.h"
#include <QGridLayout>
#include <QVariant>
#include <QToolButton>
#include <QActionGroup>
#include <QTimer>
#include <QHeaderView>
#include <KMenu>
#include <KConfig>
#include <KDebug>
#include <KIconLoader>
#include <KLineEdit>
#include <KIcon>
#include <KLocale>
#include <libkdepim/messagestatus.h>
namespace KMail
{
namespace MessageListView
{
namespace Core
{
Widget::Widget( QWidget *pParent )
: QWidget( pParent ),
mStorageModel( 0 ),
mAggregation( 0 ),
mTheme( 0 ),
mFilter( 0 ),
mStorageUsesPrivateTheme( false ),
mStorageUsesPrivateAggregation( false ),
mStorageUsesPrivateSortOrder( false )
{
Manager::registerWidget( this );
setAutoFillBackground( true );
QGridLayout * g = new QGridLayout( this );
g->setMargin( 2 ); // use a smaller default
g->setSpacing( 2 );
mSearchEdit = new KLineEdit( this );
mSearchEdit->setClickMessage( i18nc( "Search for messages.", "Search" ) );
mSearchEdit->setClearButtonShown( true );
connect( mSearchEdit, SIGNAL( textEdited( const QString & ) ),
SLOT( searchEditTextEdited( const QString & ) ) );
connect( mSearchEdit, SIGNAL( clearButtonClicked() ),
SLOT( searchEditClearButtonClicked() ) );
g->addWidget( mSearchEdit, 0, 0 );
// The status filter button
mStatusFilterButton = new QToolButton( this );
mStatusFilterButton->setIcon( KIcon( "system-run" ) );
mStatusFilterButton->setIconSize( QSize( KIconLoader::SizeSmall, KIconLoader::SizeSmall ) );
mStatusFilterButton->setText( i18n( "Filter by Status" ) );
mStatusFilterButton->setToolTip( mStatusFilterButton->text() );
KMenu * menu = new KMenu( this );
connect( menu, SIGNAL( aboutToShow() ),
SLOT( statusMenuAboutToShow() ) );
mStatusFilterButton->setMenu( menu );
mStatusFilterButton->setPopupMode( QToolButton::InstantPopup );
g->addWidget( mStatusFilterButton, 0, 1 );
// The "Open Full Search" button
QToolButton * tb = new QToolButton( this );
tb->setIcon( KIcon( "edit-find-mail" ) );
tb->setText( i18n( "Open Full Search" ) );
tb->setToolTip( tb->text() );
g->addWidget( tb, 0, 2 );
connect( tb, SIGNAL( clicked() ),
this, SIGNAL( fullSearchRequest() ) );
// The sort order menu
mSortOrderButton = new QToolButton( this );
mSortOrderButton->setIcon( KIcon( "view-sort-ascending" ) );
mSortOrderButton->setIconSize( QSize( KIconLoader::SizeSmall, KIconLoader::SizeSmall ) );
mSortOrderButton->setText( i18n( "Change Sort Order" ) );
mSortOrderButton->setToolTip( mSortOrderButton->text() );
menu = new KMenu( this );
connect( menu, SIGNAL( aboutToShow() ),
SLOT( sortOrderMenuAboutToShow() ) );
mSortOrderButton->setMenu( menu );
mSortOrderButton->setPopupMode( QToolButton::InstantPopup );
g->addWidget( mSortOrderButton, 0, 3 );
// The Aggregation menu
mAggregationButton = new QToolButton( this );
mAggregationButton->setIcon( KIcon( "view-process-tree" ) ); // view-list-tree is also ok
mAggregationButton->setIconSize( QSize( KIconLoader::SizeSmall, KIconLoader::SizeSmall ) );
mAggregationButton->setText( i18n( "Select Aggregation Mode" ) );
mAggregationButton->setToolTip( mAggregationButton->text() );
menu = new KMenu( this );
connect( menu, SIGNAL( aboutToShow() ),
SLOT( aggregationMenuAboutToShow() ) );
mAggregationButton->setMenu( menu );
mAggregationButton->setPopupMode( QToolButton::InstantPopup );
g->addWidget( mAggregationButton, 0, 4 );
// The Theme menu
mThemeButton = new QToolButton( this );
mThemeButton->setIcon( KIcon( "view-preview" ) );
mThemeButton->setIconSize( QSize( KIconLoader::SizeSmall, KIconLoader::SizeSmall ) );
mThemeButton->setText( i18n( "Select View Appearance (Theme)" ) );
mThemeButton->setToolTip( mThemeButton->text() );
menu = new KMenu( this );
connect( menu, SIGNAL( aboutToShow() ),
SLOT( themeMenuAboutToShow() ) );
mThemeButton->setMenu( menu );
mThemeButton->setPopupMode( QToolButton::InstantPopup );
g->addWidget( mThemeButton, 0, 5 );
mView = new View( this );
mView->setSortOrder( &mSortOrder );
g->addWidget( mView, 1, 0, 1, 6 );
connect( mView->header(), SIGNAL( sectionClicked( int ) ),
SLOT( slotViewHeaderSectionClicked( int ) ) );
g->setRowStretch( 1, 1 );
g->setColumnStretch( 0, 1 );
mSortOrderButton->setEnabled( false );
mAggregationButton->setEnabled( false );
mThemeButton->setEnabled( false );
mSearchEdit->setEnabled( false );
mStatusFilterButton->setEnabled( false );
mSearchTimer = 0;
}
Widget::~Widget()
{
mView->setStorageModel( 0 );
Manager::unregisterWidget( this );
delete mSearchTimer;
delete mTheme;
delete mAggregation;
delete mFilter;
delete mStorageModel;
}
KPIM::MessageStatus Widget::currentFilterStatus() const
{
if ( !mFilter )
return KPIM::MessageStatus();
KPIM::MessageStatus ret;
ret.fromQInt32( mFilter->statusMask() );
return ret;
}
QString Widget::currentFilterSearchString() const
{
if ( !mFilter )
return QString();
return mFilter->searchString();
}
QString Widget::currentFilterTagId() const
{
if ( !mFilter )
return QString();
return mFilter->tagId();
}
void Widget::setDefaultAggregationForStorageModel( const StorageModel * storageModel )
{
const Aggregation * opt = Manager::instance()->aggregationForStorageModel( storageModel, &mStorageUsesPrivateAggregation );
Q_ASSERT( opt );
delete mAggregation;
mAggregation = new Aggregation( *opt );
mView->setAggregation( mAggregation );
mLastAggregationId = opt->id();
}
void Widget::setDefaultThemeForStorageModel( const StorageModel * storageModel )
{
const Theme * opt = Manager::instance()->themeForStorageModel( storageModel, &mStorageUsesPrivateTheme );
Q_ASSERT( opt );
delete mTheme;
mTheme = new Theme( *opt );
mView->setTheme( mTheme );
mLastThemeId = opt->id();
}
void Widget::checkSortOrder( const StorageModel *storageModel )
{
if ( storageModel && mAggregation && !mSortOrder.validForAggregation( mAggregation ) ) {
kDebug() << "Could not restore sort order for folder" << storageModel->id();
mSortOrder = SortOrder::defaultForAggregation( mAggregation, mSortOrder );
// Change the global sort order if the sort order didn't fit the global aggregation.
// Otherwise, if it is a per-folder aggregation, make the sort order per-folder too.
if ( mStorageUsesPrivateAggregation )
mStorageUsesPrivateSortOrder = true;
if ( mStorageModel ) {
Manager::instance()->saveSortOrderForStorageModel( storageModel, mSortOrder,
mStorageUsesPrivateSortOrder );
}
switchMessageSorting( mSortOrder.messageSorting(), mSortOrder.messageSortDirection(), -1 );
}
}
void Widget::setDefaultSortOrderForStorageModel( const StorageModel * storageModel )
{
// Load the sort order from config and update column headers
mSortOrder = Manager::instance()->sortOrderForStorageModel( storageModel, &mStorageUsesPrivateSortOrder );
switchMessageSorting( mSortOrder.messageSorting(), mSortOrder.messageSortDirection(), -1 );
checkSortOrder( storageModel );
}
void Widget::setStorageModel( StorageModel * storageModel, PreSelectionMode preSelectionMode )
{
if ( storageModel == mStorageModel )
return; // nuthin to do here
setDefaultAggregationForStorageModel( storageModel );
setDefaultThemeForStorageModel( storageModel );
setDefaultSortOrderForStorageModel( storageModel );
if ( mSearchTimer )
{
mSearchTimer->stop();
delete mSearchTimer;
mSearchTimer = 0;
}
mSearchEdit->setText( QString() );
if ( mFilter )
{
delete mFilter;
mFilter = 0;
mStatusFilterButton->setIcon( SmallIcon( "system-run" ) );
mView->model()->setFilter( 0 );
}
StorageModel * oldModel = mStorageModel;
mStorageModel = storageModel;
mView->setStorageModel( mStorageModel, preSelectionMode );
delete oldModel;
mStatusFilterButton->setEnabled( mStorageModel );
mSortOrderButton->setEnabled( mStorageModel );
mAggregationButton->setEnabled( mStorageModel );
mThemeButton->setEnabled( mStorageModel );
mSearchEdit->setEnabled( mStorageModel );
}
void Widget::themeMenuAboutToShow()
{
if ( !mStorageModel )
return;
KMenu * menu = dynamic_cast< KMenu * >( sender() );
if ( !menu )
return;
menu->clear();
menu->addTitle( i18n( "Theme" ) );
QActionGroup * grp = new QActionGroup( menu );
const QHash< QString, Theme * > & themes = Manager::instance()->themes();
QAction * act;
QList< const Theme * > sortedThemes;
for ( QHash< QString, Theme * >::ConstIterator ci = themes.constBegin(); ci != themes.constEnd(); ++ci )
{
int idx = 0;
int cnt = sortedThemes.count();
while ( idx < cnt )
{
if ( sortedThemes.at( idx )->name() > ( *ci )->name() )
{
sortedThemes.insert( idx, *ci );
break;
}
idx++;
}
if ( idx == cnt )
sortedThemes.append( *ci );
}
for ( QList< const Theme * >::ConstIterator it = sortedThemes.constBegin(); it != sortedThemes.constEnd(); ++it )
{
act = menu->addAction( ( *it )->name() );
act->setCheckable( true );
grp->addAction( act );
act->setChecked( mLastThemeId == ( *it )->id() );
act->setData( QVariant( ( *it )->id() ) );
connect( act, SIGNAL( triggered( bool ) ),
SLOT( themeSelected( bool ) ) );
}
menu->addSeparator();
act = menu->addAction( i18n( "Folder Always Uses This Theme" ) );
act->setCheckable( true );
act->setChecked( mStorageUsesPrivateTheme );
connect( act, SIGNAL( triggered( bool ) ),
SLOT( setPrivateThemeForStorage() ) );
menu->addSeparator();
act = menu->addAction( i18n( "Configure..." ) );
connect( act, SIGNAL( triggered( bool ) ),
SLOT( configureThemes() ) );
}
void Widget::setPrivateThemeForStorage()
{
if ( !mStorageModel )
return;
Q_ASSERT( mTheme );
mStorageUsesPrivateTheme = !mStorageUsesPrivateTheme;
Manager::instance()->saveThemeForStorageModel( mStorageModel, mTheme->id(), mStorageUsesPrivateTheme );
}
void Widget::setPrivateSortOrderForStorage()
{
if ( !mStorageModel )
return;
mStorageUsesPrivateSortOrder = !mStorageUsesPrivateSortOrder;
Manager::instance()->saveSortOrderForStorageModel( mStorageModel, mSortOrder,
mStorageUsesPrivateSortOrder );
}
void Widget::configureThemes()
{
// Show customization dialog
Manager::instance()->showConfigureThemesDialog( this, mLastThemeId );
}
void Widget::themeSelected( bool )
{
if ( !mStorageModel )
return; // nuthin to do
QAction * act = dynamic_cast< QAction * >( sender() );
if ( !act )
return;
QVariant v = act->data();
QString id = v.toString();
if ( id.isEmpty() )
return;
const Theme * opt = Manager::instance()->theme( id );
delete mTheme;
mTheme = new Theme( *opt );
mView->setTheme( mTheme );
mLastThemeId = opt->id();
//mStorageUsesPrivateTheme = false;
Manager::instance()->saveThemeForStorageModel( mStorageModel, opt->id(), mStorageUsesPrivateTheme );
mView->reload();
}
void Widget::aggregationMenuAboutToShow()
{
KMenu * menu = dynamic_cast< KMenu * >( sender() );
if ( !menu )
return;
menu->clear();
menu->addTitle( i18n( "Aggregation" ) );
QActionGroup * grp = new QActionGroup( menu );
const QHash< QString, Aggregation * > & aggregations = Manager::instance()->aggregations();
QAction * act;
QList< const Aggregation * > sortedAggregations;
for ( QHash< QString, Aggregation * >::ConstIterator ci = aggregations.constBegin(); ci != aggregations.constEnd(); ++ci )
{
int idx = 0;
int cnt = sortedAggregations.count();
while ( idx < cnt )
{
if ( sortedAggregations.at( idx )->name() > ( *ci )->name() )
{
sortedAggregations.insert( idx, *ci );
break;
}
idx++;
}
if ( idx == cnt )
sortedAggregations.append( *ci );
}
for ( QList< const Aggregation * >::ConstIterator it = sortedAggregations.constBegin(); it != sortedAggregations.constEnd(); ++it )
{
act = menu->addAction( ( *it )->name() );
act->setCheckable( true );
grp->addAction( act );
act->setChecked( mLastAggregationId == ( *it )->id() );
act->setData( QVariant( ( *it )->id() ) );
connect( act, SIGNAL( triggered( bool ) ),
SLOT( aggregationSelected( bool ) ) );
}
menu->addSeparator();
act = menu->addAction( i18n( "Folder Always Uses This Aggregation" ) );
act->setCheckable( true );
act->setChecked( mStorageUsesPrivateAggregation );
connect( act, SIGNAL( triggered( bool ) ),
SLOT( setPrivateAggregationForStorage() ) );
menu->addSeparator();
act = menu->addAction( i18n( "Configure..." ) );
act->setData( QVariant( QString() ) );
connect( act, SIGNAL( triggered( bool ) ),
SLOT( aggregationSelected( bool ) ) );
}
void Widget::setPrivateAggregationForStorage()
{
if ( !mStorageModel )
return;
Q_ASSERT( mAggregation );
mStorageUsesPrivateAggregation = !mStorageUsesPrivateAggregation;
Manager::instance()->saveAggregationForStorageModel( mStorageModel, mAggregation->id(), mStorageUsesPrivateAggregation );
}
void Widget::aggregationSelected( bool )
{
QAction * act = dynamic_cast< QAction * >( sender() );
if ( !act )
return;
QVariant v = act->data();
QString id = v.toString();
if ( id.isEmpty() )
{
// Show customization dialog
Manager::instance()->showConfigureAggregationsDialog( this, mLastAggregationId );
return;
}
if ( !mStorageModel )
return; // nuthin to do
const Aggregation * opt = Manager::instance()->aggregation( id );
delete mAggregation;
mAggregation = new Aggregation( *opt );
mView->setAggregation( mAggregation );
mLastAggregationId = opt->id();
//mStorageUsesPrivateAggregation = false;
Manager::instance()->saveAggregationForStorageModel( mStorageModel, opt->id(), mStorageUsesPrivateAggregation );
// The sort order might not be valid anymore for this aggregation
checkSortOrder( mStorageModel );
mView->reload();
}
void Widget::sortOrderMenuAboutToShow()
{
if ( !mAggregation )
return;
KMenu * menu = dynamic_cast< KMenu * >( sender() );
if ( !menu )
return;
menu->clear();
menu->addTitle( i18n( "Message Sort Order" ) );
QActionGroup * grp;
QAction * act;
QList< QPair< QString, int > > options;
QList< QPair< QString, int > >::ConstIterator it;
grp = new QActionGroup( menu );
options = SortOrder::enumerateMessageSortingOptions( mAggregation->threading() );
for ( it = options.constBegin(); it != options.constEnd(); ++it )
{
act = menu->addAction( ( *it ).first );
act->setCheckable( true );
grp->addAction( act );
act->setChecked( mSortOrder.messageSorting() == ( *it ).second );
act->setData( QVariant( ( *it ).second ) );
}
connect( grp, SIGNAL( triggered( QAction * ) ),
SLOT( messageSortingSelected( QAction * ) ) );
options = SortOrder::enumerateMessageSortDirectionOptions( mSortOrder.messageSorting() );
if ( options.size() >= 2 )
{
menu->addTitle( i18n( "Message Sort Direction" ) );
grp = new QActionGroup( menu );
for ( it = options.constBegin(); it != options.constEnd(); ++it )
{
act = menu->addAction( ( *it ).first );
act->setCheckable( true );
grp->addAction( act );
act->setChecked( mSortOrder.messageSortDirection() == ( *it ).second );
act->setData( QVariant( ( *it ).second ) );
}
connect( grp, SIGNAL( triggered( QAction * ) ),
SLOT( messageSortDirectionSelected( QAction * ) ) );
}
options = SortOrder::enumerateGroupSortingOptions( mAggregation->grouping() );
if ( options.size() >= 2 )
{
menu->addTitle( i18n( "Group Sort Order" ) );
grp = new QActionGroup( menu );
for ( it = options.constBegin(); it != options.constEnd(); ++it )
{
act = menu->addAction( ( *it ).first );
act->setCheckable( true );
grp->addAction( act );
act->setChecked( mSortOrder.groupSorting() == ( *it ).second );
act->setData( QVariant( ( *it ).second ) );
}
connect( grp, SIGNAL( triggered( QAction * ) ),
SLOT( groupSortingSelected( QAction * ) ) );
}
options = SortOrder::enumerateGroupSortDirectionOptions( mAggregation->grouping(),
mSortOrder.groupSorting() );
if ( options.size() >= 2 )
{
menu->addTitle( i18n( "Group Sort Direction" ) );
grp = new QActionGroup( menu );
for ( it = options.constBegin(); it != options.constEnd(); ++it )
{
act = menu->addAction( ( *it ).first );
act->setCheckable( true );
grp->addAction( act );
act->setChecked( mSortOrder.groupSortDirection() == ( *it ).second );
act->setData( QVariant( ( *it ).second ) );
}
connect( grp, SIGNAL( triggered( QAction * ) ),
SLOT( groupSortDirectionSelected( QAction * ) ) );
}
menu->addSeparator();
act = menu->addAction( i18n( "Folder Always Uses This Sort Order" ) );
act->setCheckable( true );
act->setChecked( mStorageUsesPrivateSortOrder );
connect( act, SIGNAL( triggered( bool ) ),
SLOT( setPrivateSortOrderForStorage() ) );
}
void Widget::switchMessageSorting( SortOrder::MessageSorting messageSorting,
SortOrder::SortDirection sortDirection,
int logicalHeaderColumnIndex )
{
mSortOrder.setMessageSorting( messageSorting );
mSortOrder.setMessageSortDirection( sortDirection );
// If the logicalHeaderColumnIndex was specified then we already know which
// column we should set the sort indicator to. If it wasn't specified (it's -1)
// then we need to find it out in the theme.
if ( logicalHeaderColumnIndex == -1 )
{
// try to find the specified message sorting in the theme columns
const QList< Theme::Column * > & columns = mTheme->columns();
int idx = 0;
foreach( const Theme::Column* column, columns )
{
if ( !mView->header()->isSectionHidden( idx ) )
{
if ( ( column->messageSorting() == messageSorting ||
( column->messageSorting() == SortOrder::SortMessagesBySenderOrReceiver ) ) &&
( messageSorting == SortOrder::SortMessagesByReceiver ||
messageSorting == SortOrder::SortMessagesBySender ) )
{
// found a visible column with this message sorting
logicalHeaderColumnIndex = idx;
break;
}
}
++idx;
}
}
if ( logicalHeaderColumnIndex == -1 )
{
// not found: either not a column-based sorting or the related column is hidden
mView->header()->setSortIndicatorShown( false );
return;
}
mView->header()->setSortIndicatorShown( true );
if ( sortDirection == SortOrder::Ascending )
mView->header()->setSortIndicator( logicalHeaderColumnIndex, Qt::AscendingOrder );
else
mView->header()->setSortIndicator( logicalHeaderColumnIndex, Qt::DescendingOrder );
}
void Widget::messageSortingSelected( QAction *action )
{
if ( !mAggregation )
return;
if ( !action )
return;
bool ok;
SortOrder::MessageSorting ord = static_cast< SortOrder::MessageSorting >( action->data().toInt( &ok ) );
if ( !ok )
return;
switchMessageSorting( ord, mSortOrder.messageSortDirection(), -1 );
Manager::instance()->saveSortOrderForStorageModel( mStorageModel, mSortOrder,
mStorageUsesPrivateSortOrder );
mView->reload();
}
void Widget::messageSortDirectionSelected( QAction *action )
{
if ( !mAggregation )
return;
if ( !action )
return;
bool ok;
SortOrder::SortDirection ord = static_cast< SortOrder::SortDirection >( action->data().toInt( &ok ) );
if ( !ok )
return;
switchMessageSorting( mSortOrder.messageSorting(), ord, -1 );
Manager::instance()->saveSortOrderForStorageModel( mStorageModel, mSortOrder,
mStorageUsesPrivateSortOrder );
mView->reload();
}
void Widget::groupSortingSelected( QAction *action )
{
if ( !mAggregation )
return;
if ( !action )
return;
bool ok;
SortOrder::GroupSorting ord = static_cast< SortOrder::GroupSorting >( action->data().toInt( &ok ) );
if ( !ok )
return;
mSortOrder.setGroupSorting( ord );
Manager::instance()->saveSortOrderForStorageModel( mStorageModel, mSortOrder,
mStorageUsesPrivateSortOrder );
mView->reload();
}
void Widget::groupSortDirectionSelected( QAction *action )
{
if ( !mAggregation )
return;
if ( !action )
return;
bool ok;
SortOrder::SortDirection ord = static_cast< SortOrder::SortDirection >( action->data().toInt( &ok ) );
if ( !ok )
return;
mSortOrder.setGroupSortDirection( ord );
Manager::instance()->saveSortOrderForStorageModel( mStorageModel, mSortOrder,
mStorageUsesPrivateSortOrder );
mView->reload();
}
void Widget::slotViewHeaderSectionClicked( int logicalIndex )
{
if ( !mTheme )
return;
if ( !mAggregation )
return;
if ( logicalIndex >= mTheme->columns().count() )
return;
const Theme::Column * column = mTheme->column( logicalIndex );
if ( !column )
return; // should never happen...
if ( column->messageSorting() == SortOrder::NoMessageSorting )
return; // this is a null op.
if ( mSortOrder.messageSorting() == column->messageSorting() )
{
// switch sort direction
if ( mSortOrder.messageSortDirection() == SortOrder::Ascending )
switchMessageSorting( mSortOrder.messageSorting(), SortOrder::Descending, logicalIndex );
else
switchMessageSorting( mSortOrder.messageSorting(), SortOrder::Ascending, logicalIndex );
} else {
// keep sort direction but switch sort order
switchMessageSorting( column->messageSorting(), mSortOrder.messageSortDirection(), logicalIndex );
}
Manager::instance()->saveSortOrderForStorageModel( mStorageModel, mSortOrder,
mStorageUsesPrivateSortOrder );
mView->reload();
}
void Widget::themesChanged()
{
setDefaultThemeForStorageModel( mStorageModel );
mView->reload();
}
void Widget::aggregationsChanged()
{
setDefaultAggregationForStorageModel( mStorageModel );
checkSortOrder( mStorageModel );
mView->reload();
}
void Widget::statusMenuAboutToShow()
{
KMenu * menu = dynamic_cast< KMenu * >( sender() );
if ( !menu )
return;
menu->clear();
menu->addTitle( i18n( "Message Status" ) );
QAction * act;
qint32 statusMask = mFilter ? mFilter->statusMask() : 0;
KPIM::MessageStatus stat;
stat.fromQInt32( statusMask );
QActionGroup * grp = new QActionGroup( menu );
// FIXME: Use cached icons from manager ?
act = menu->addAction( i18n( "Any Status" ) );
act->setIcon( SmallIcon("system-run") );
act->setCheckable( true );
act->setChecked( ( statusMask == 0 ) && currentFilterTagId().isEmpty() );
act->setData( QVariant( static_cast< int >( 0 ) ) );
grp->addAction( act );
menu->addSeparator();
act = menu->addAction( i18nc( "@action:inmenu Status of a message", "New" ) );
act->setIcon( SmallIcon("mail-unread-new") );
act->setCheckable( true );
act->setChecked( stat.isNew() && ( !stat.isUnread() ) );
act->setData( QVariant( static_cast< int >( KPIM::MessageStatus::statusNew().toQInt32() ) ) );
grp->addAction( act );
act = menu->addAction( i18nc( "@action:inmenu Status of a message", "Unread" ) );
act->setIcon( SmallIcon("mail-unread") );
act->setCheckable( true );
act->setChecked( stat.isUnread() );
act->setData( QVariant( static_cast< int >( KPIM::MessageStatus::statusUnread().toQInt32() | KPIM::MessageStatus::statusNew().toQInt32() ) ) );
grp->addAction( act );
act = menu->addAction( i18nc( "@action:inmenu Status of a message", "Replied" ) );
act->setIcon( SmallIcon("mail-replied") );
act->setCheckable( true );
act->setChecked( stat.isReplied() );
act->setData( QVariant( static_cast< int >( KPIM::MessageStatus::statusReplied().toQInt32() ) ) );
grp->addAction( act );
act = menu->addAction( i18nc( "@action:inmenu Status of a message", "Forwarded" ) );
act->setIcon( SmallIcon("mail-forwarded") );
act->setCheckable( true );
act->setChecked( stat.isForwarded() );
act->setData( QVariant( static_cast< int >( KPIM::MessageStatus::statusForwarded().toQInt32() ) ) );
grp->addAction( act );
act = menu->addAction( i18nc( "@action:inmenu Status of a message", "Important") );
act->setIcon( SmallIcon("emblem-important") );
act->setCheckable( true );
act->setChecked( stat.isImportant() );
act->setData( QVariant( static_cast< int >( KPIM::MessageStatus::statusImportant().toQInt32() ) ) );
grp->addAction( act );
act = menu->addAction( i18n( "Action Item" ) );
act->setIcon( SmallIcon("mail-task") );
act->setCheckable( true );
act->setChecked( stat.isToAct() );
act->setData( QVariant( static_cast< int >( KPIM::MessageStatus::statusToAct().toQInt32() ) ) );
grp->addAction( act );
act = menu->addAction( i18n( "Watched" ) );
act->setIcon( SmallIcon("mail-thread-watch") );
act->setCheckable( true );
act->setChecked( stat.isWatched() );
act->setData( QVariant( static_cast< int >( KPIM::MessageStatus::statusWatched().toQInt32() ) ) );
grp->addAction( act );
act = menu->addAction( i18n( "Ignored" ) );
act->setIcon( SmallIcon("mail-thread-ignored") );
act->setCheckable( true );
act->setChecked( stat.isIgnored() );
act->setData( QVariant( static_cast< int >( KPIM::MessageStatus::statusIgnored().toQInt32() ) ) );
grp->addAction( act );
act = menu->addAction( i18n( "Has Attachment" ) );
act->setIcon( SmallIcon("mail-attachment") );
act->setCheckable( true );
act->setChecked( stat.hasAttachment() );
act->setData( QVariant( static_cast< int >( KPIM::MessageStatus::statusHasAttachment().toQInt32() ) ) );
grp->addAction( act );
act = menu->addAction( i18n( "Spam" ) );
act->setIcon( SmallIcon("mail-mark-junk") );
act->setCheckable( true );
act->setChecked( stat.isSpam() );
act->setData( QVariant( static_cast< int >( KPIM::MessageStatus::statusSpam().toQInt32() ) ) );
grp->addAction( act );
act = menu->addAction( i18n( "Ham" ) );
act->setIcon( SmallIcon("mail-mark-notjunk") );
act->setCheckable( true );
act->setChecked( stat.isHam() );
act->setData( QVariant( static_cast< int >( KPIM::MessageStatus::statusHam().toQInt32() ) ) );
grp->addAction( act );
connect( grp, SIGNAL( triggered( QAction * ) ),
SLOT( statusSelected( QAction * ) ) );
grp = fillMessageTagMenu( menu );
if ( grp )
connect( grp, SIGNAL( triggered( QAction * ) ),
SLOT( tagIdSelected( QAction * ) ) );
}
QActionGroup * Widget::fillMessageTagMenu( KMenu * /*menu*/ )
{
// nothing here: must be overridden in derived classes
return 0;
}
void Widget::tagIdSelected( QAction *action )
{
QString tagId = action->data().toString();
// Here we arbitrairly set the status to 0, though we *could* allow filtering
// by status AND tag...
if ( mFilter )
mFilter->setStatusMask( 0 );
if ( tagId.isEmpty() )
{
if ( mFilter )
{
if ( mFilter->isEmpty() )
{
delete mFilter;
mFilter = 0;
}
}
} else {
if ( !mFilter )
mFilter = new Filter();
mFilter->setTagId( tagId );
}
mStatusFilterButton->setIcon( action->icon() );
mView->model()->setFilter( mFilter );
}
void Widget::statusSelected( QAction *action )
{
bool ok;
qint32 additionalStatusMask = static_cast< qint32 >( action->data().toInt( &ok ) );
if ( !ok )
return;
// Here we override the whole status at once.
// This is a restriction but at least a couple of people
// are telling me that this way it's more usable. Two are more than me
// so here we go :)
qint32 statusMask = 0; //mFilter ? mFilter->statusMask() : 0; <-- this would "or" with the existing mask instead
// We also arbitrairly set tagId to an empty string, though we *could* allow filtering
// by status AND tag...
if ( mFilter )
mFilter->setTagId( QString() );
if ( additionalStatusMask == 0)
{
if ( mFilter )
{
mFilter->setStatusMask( 0 );
if ( mFilter->isEmpty() )
{
delete mFilter;
mFilter = 0;
}
}
} else {
if ( statusMask & additionalStatusMask )
{
// already have this status bit (this actually never happens because of the override above)
if ( mFilter )
{
mFilter->setStatusMask( statusMask & ~additionalStatusMask );
if ( mFilter->isEmpty() )
{
delete mFilter;
mFilter = 0;
}
} // else nothing to remove (but something weird happened in the code above...)
} else {
// don't have this status bit
if ( !mFilter )
mFilter = new Filter();
mFilter->setStatusMask( statusMask | additionalStatusMask );
}
}
mStatusFilterButton->setIcon( action->icon() );
mView->model()->setFilter( mFilter );
}
void Widget::searchEditTextEdited( const QString & )
{
// This slot is called whenever the user edits the search QLineEdit.
// Since the user is likely to type more than one character
// so we start the real search after a short delay in order to catch
// multiple textEdited() signals.
if ( !mSearchTimer )
{
mSearchTimer = new QTimer( this );
connect( mSearchTimer, SIGNAL( timeout() ),
SLOT( searchTimerFired() ) );
} else {
mSearchTimer->stop(); // eventually
}
mSearchTimer->setSingleShot( true );
mSearchTimer->start( 1000 );
}
void Widget::searchTimerFired()
{
// A search is pending.
if ( mSearchTimer )
mSearchTimer->stop();
if ( !mFilter )
mFilter = new Filter();
QString text = mSearchEdit->text();
mFilter->setSearchString( text );
if ( mFilter->isEmpty() )
{
delete mFilter;
mFilter = 0;
}
mView->model()->setFilter( mFilter );
}
void Widget::searchEditClearButtonClicked()
{
if ( !mFilter )
return;
delete mFilter;
mFilter = 0;
mStatusFilterButton->setIcon( SmallIcon( "system-run" ) );
mView->model()->setFilter( mFilter );
}
void Widget::viewMessageSelected( MessageItem * )
{
}
void Widget::viewMessageActivated( MessageItem * )
{
}
void Widget::viewSelectionChanged()
{
}
void Widget::viewMessageListContextPopupRequest( const QList< MessageItem * > &, const QPoint & )
{
}
void Widget::viewGroupHeaderContextPopupRequest( GroupHeaderItem *, const QPoint & )
{
}
void Widget::viewDragEnterEvent( QDragEnterEvent * )
{
}
void Widget::viewDragMoveEvent( QDragMoveEvent * )
{
}
void Widget::viewDropEvent( QDropEvent * )
{
}
void Widget::viewStartDragRequest()
{
}
void Widget::viewJobBatchStarted()
{
}
void Widget::viewJobBatchTerminated()
{
}
void Widget::viewMessageStatusChangeRequest( MessageItem *msg, const KPIM::MessageStatus &set, const KPIM::MessageStatus &clear )
{
Q_UNUSED( msg );
Q_UNUSED( set );
Q_UNUSED( clear );
}
} // namespace Core
} // namespace MessageListView
} // namespace KMail