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.
 
 
 

481 lines
17 KiB

/*
Copyright (c) 2001 Heiko Hund <heiko@ist.eigentlich.net>
Copyright (c) 2001 Thorsten Zachmann <T.Zachmann@zagge.de>
Copyright (c) 2008 Thomas McGuire <Thomas.McGuire@gmx.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 "kmpopfiltercnfrmdlg.h"
#include "kmmsgbase.h"
#include "kmmessage.h"
#include <QButtonGroup>
#include <QCheckBox>
#include <QGroupBox>
#include <QHeaderView>
#include <QKeyEvent>
#include <QLabel>
#include <QLayout>
#include <QRadioButton>
#include <QSignalMapper>
#include <QTimer>
#include <QVBoxLayout>
#include <klocale.h>
#include <kio/global.h>
#include <kmime/kmime_dateformatter.h>
#include <assert.h>
KMPopHeadersView::KMPopHeadersView( QWidget *parent,
KMPopFilterCnfrmDlg *dialog )
: QTreeWidget( parent )
{
assert( dialog );
mDialog = dialog;
QStringList headerNames;
headerNames << "" << "" << ""
<< i18nc("@title:column Column containing message subjects", "Subject")
<< i18nc("@title:column", "Sender") << i18nc("@title:column", "Receiver")
<< i18nc("@title:column", "Date") << i18nc("@title:column", "Size");
QTreeWidgetItem *headerItem = new QTreeWidgetItem( headerNames );
headerItem->setTextAlignment( 0, Qt::AlignHCenter );
headerItem->setTextAlignment( 1, Qt::AlignHCenter );
headerItem->setTextAlignment( 2, Qt::AlignHCenter );
headerItem->setTextAlignment( 7, Qt::AlignRight );
headerItem->setToolTip( 0, i18nc("@action:button", "Download all messages now") );
headerItem->setToolTip( 1, i18nc("@action:button", "Download all messages later") );
headerItem->setToolTip( 2, i18nc("@action:button", "Delete all messages") );
headerItem->setIcon( 0, KIcon( "mail-download-now" ) );
headerItem->setIcon( 1, KIcon( "mail-download-later" ) );
headerItem->setIcon( 2, KIcon( "edit-delete" ) );
setHeaderItem( headerItem );
header()->setResizeMode( QHeaderView::Interactive );
header()->setResizeMode( 0, QHeaderView::Fixed );
header()->setResizeMode( 1, QHeaderView::Fixed );
header()->setResizeMode( 2, QHeaderView::Fixed );
//### Why is this so horribly broken?? Suddenly, a horizontal scrollbar
// is there, the sections can't be resized properly (other columns
// suddenly change size, resizing stops) etc.
// Disable it for now.
//header()->setResizeMode( 3, QHeaderView::Stretch );
header()->setStretchLastSection( false );
setColumnWidth( 0, 22 ); // Download Now icon
setColumnWidth( 1, 22 ); // Download Later icon
setColumnWidth( 2, 22 ); // Delete icon
setColumnWidth( 3, 180 ); // Subject
setColumnWidth( 4, 140 ); // Sender
setColumnWidth( 5, 140); // Receiver
setColumnWidth( 6, 130 ); // Date
setColumnWidth( 7, 80 ); // Size
setAllColumnsShowFocus( true );
setIndentation( 0 );
header()->setSortIndicator( 7, Qt::DescendingOrder );
setSelectionMode( QAbstractItemView::ExtendedSelection );
setAlternatingRowColors ( true );
// We can not enable automatic sorting, because that would make the icon
// columns sortable. We want to change the action of all items when an icon
// column is clicked instead
header()->setSortIndicatorShown( true );
header()->setClickable( true );
connect( header(), SIGNAL( sectionClicked(int) ),
this, SLOT( slotSectionClicked(int) ) );
// This code relies on the fact that radiobuttons are the first three
// columns for easier Column-Action mapping it does not necessarily be
// true - you could redefine mapToColumn and mapToAction to eg. shift
// those numbers by 1
assert( 0 == Down );
assert( 1 == Later );
assert( 2 == Delete );
//we rely on fixed column order, so we forbid this
header()->setMovable( false );
}
KMPopHeadersView::~KMPopHeadersView()
{
}
// Handle keystrokes - Left and Right key select previous/next
// action correspondingly
void KMPopHeadersView::keyPressEvent( QKeyEvent *e )
{
if ( e->key() != Qt::Key_Left && e->key() != Qt::Key_Right ) {
QTreeWidget::keyPressEvent( e );
return;
}
// Loop through all selected items change the selected action to the action
// to the right/left if that key was pressed
foreach ( QTreeWidgetItem *item, selectedItems() ) {
KMPopHeadersViewItem *popItem
= dynamic_cast<KMPopHeadersViewItem*>( item );
assert( popItem );
assert( mDialog );
int newAction = popItem->action();
if ( e->key() == Qt::Key_Left ) {
// here we rely on the fact that the leftmost action is equal to 0!
if ( popItem->action() )
newAction = popItem->action() - 1;
}
else if ( e->key() == Qt::Key_Right ) {
//here we rely on the fact that right most action is one less than NoAction!
if ( popItem->action() < NoAction - 1 )
newAction = popItem->action() + 1;
}
popItem->setAction( static_cast<KMPopFilterAction>( newAction ) );
mDialog->setAction( item, popItem->action() );
assert( popItem->action() >= 0 && popItem->action() < NoAction );
}
}
void KMPopHeadersView::slotRadioButtonClicked( QTreeWidgetItem* item, int column ) {
assert( item && column >= 0 && column < NoAction );
mDialog->setAction( item, mapToAction( column ) );
// If the user selected some items, but changed an unselected one, he probebly
// doesn't want to change the actions of the selected ones
if ( !item->isSelected() )
return;
// The user has selected at least one item and clicked on a radio button.
// Toogle all radiobuttons of the other selected items as well. This way,
// the user can quickly set many actions at once.
foreach ( QTreeWidgetItem *selectedItem, selectedItems() ) {
KMPopHeadersViewItem *popItem =
dynamic_cast<KMPopHeadersViewItem*>( selectedItem );
assert( popItem );
popItem->setAction( mapToAction( column ) );
mDialog->setAction( popItem, mapToAction( column ) );
}
}
void KMPopHeadersView::slotSectionClicked( int column )
{
// If a normal column was clicked, just sort that column
if ( column > 2 ) {
mLastSortColumn = header()->sortIndicatorSection();
mLastSortOrder = header()->sortIndicatorOrder();
sortByColumn( mLastSortColumn, mLastSortOrder );
}
// If one of the action columns was clicked, change the action of all items
else {
for ( int i = 0; i < topLevelItemCount(); i++ ) {
KMPopHeadersViewItem *item =
static_cast<KMPopHeadersViewItem*>( topLevelItem( i ) );
item->setAction( mapToAction( column ) );
mDialog->setAction( item, mapToAction( column ) );
}
// Reset the sort indicator Qt now incorrectly set to the icon column
header()->setSortIndicator( mLastSortColumn, mLastSortOrder );
}
}
KMPopHeadersViewItem::KMPopHeadersViewItem( KMPopHeadersView *parent,
KMPopFilterAction action )
: QTreeWidgetItem( parent )
{
mParent = parent;
mAction = NoAction;
setToolTip( 0, i18nc("@info:tooltip", "Download Now") );
setToolTip( 1, i18nc("@info:tooltip", "Download Later") );
setToolTip( 2, i18nc("@info:tooltip", "Delete") );
mActionGroup = new QButtonGroup( parent );
mMapper = new QSignalMapper( parent );
for( int column = 0; column <= 2; column++ ) {
QRadioButton *button = addRadioButton( column );
mActionGroup->addButton( button, column );
// Don't accept focus, otherwise the right/left keys will incorrectly
// change the action
button->setFocusPolicy( Qt::NoFocus );
connect( button, SIGNAL( clicked(bool) ), mMapper, SLOT( map() ) );
mMapper->setMapping( button, column );
}
connect( mMapper, SIGNAL( mapped(int) ), this, SLOT( slotActionChanged(int) ) );
setAction( action );
}
// Taken and adapted from QCheckBox* KMAtmListViewItem::addCheckBox.
QRadioButton* KMPopHeadersViewItem::addRadioButton( int column )
{
// We can not call setItemWidget() on the radiobutton directly, because then
// the checkbox would be left-aligned. Therefore we create a helper widget
// with a layout to align the radiobutton to the center.
QWidget *w = new QWidget( treeWidget() );
QRadioButton *r = new QRadioButton();
QHBoxLayout *l = new QHBoxLayout();
l->insertWidget( 0, r, 0, Qt::AlignHCenter );
w->setBackgroundRole( QPalette::Base );
r->setBackgroundRole( QPalette::Base );
w->setLayout( l );
l->setMargin( 0 );
l->setSpacing( 0 );
w->show();
treeWidget()->setItemWidget( this, column, w );
return r;
}
KMPopHeadersViewItem::~KMPopHeadersViewItem()
{
}
void KMPopHeadersViewItem::slotActionChanged( int column )
{
setAction( static_cast<KMPopFilterAction>( column ) );
emit radioButtonClicked( this, column );
}
void KMPopHeadersViewItem::setAction( KMPopFilterAction action )
{
if ( action != NoAction && action != mAction ) {
if ( mAction != NoAction )
mActionGroup->button( mParent->mapToColumn( mAction ) )->setChecked( false );
mActionGroup->button( mParent->mapToColumn( action ) )->setChecked( true );
mAction = action;
}
}
bool KMPopHeadersViewItem::operator < ( const QTreeWidgetItem & other ) const
{
switch( treeWidget()->sortColumn() ) {
case 3: { // subject column
const KMPopHeadersViewItem *otherItem =
(static_cast<const KMPopHeadersViewItem*>( &other ));
QString subject1 = KMMsgBase::skipKeyword( text( 3 ).toLower() );
QString subject2 = KMMsgBase::skipKeyword( otherItem->text( 3 ).toLower() );
return subject1 < subject2;
}
case 6: // date column
return mIsoDate <
(static_cast<const KMPopHeadersViewItem*>( &other ))->mIsoDate;
case 7: // size column
return mSizeOfMessage <
(static_cast<const KMPopHeadersViewItem*>( &other ))->mSizeOfMessage;
default:
return QTreeWidgetItem::operator < ( other );
}
}
KMPopFilterCnfrmDlg::KMPopFilterCnfrmDlg( const QList<KMPopHeaders *> & headers,
const QString & account,
bool showLaterMsgs,
QWidget * parent )
: KDialog( parent )
{
setUpdatesEnabled( false );
setCaption( i18nc("@title:window", "POP Filter") );
setButtons( Ok | Help | Cancel );
setHelp( "popfilters" );
unsigned int rulesetCount = 0;
mShowLaterMsgs = showLaterMsgs;
mLowerBoxVisible = false;
QWidget *mainWidget = new QWidget( this );
setMainWidget( mainWidget );
QVBoxLayout *mainLayout = new QVBoxLayout( mainWidget );
mainLayout->setSpacing( spacingHint() );
mainLayout->setMargin( 0 );
QLabel *infoLabel = new QLabel(
i18nc( "@info", "<title>Messages to filter found on POP Account: <emphasis>%1</emphasis></title>"
"<para>The messages shown exceed the maximum size limit you defined "
"for this account.</para><para>You can select what you want to do "
"with them by checking the appropriate button.</para>",
account ), mainWidget );
mainLayout->addWidget( infoLabel );
QGroupBox *upperBox = new QGroupBox(i18nc( "@info", "Messages Exceeding Size"), mainWidget );
QVBoxLayout *upperBoxLayout = new QVBoxLayout( upperBox );
upperBox->hide();
KMPopHeadersView *upperHeadersView = new KMPopHeadersView( upperBox, this );
upperBoxLayout->addWidget( upperHeadersView );
mainLayout->addWidget( upperBox );
QGroupBox *lowerBox = new QGroupBox( i18nc("@title:group","Ruleset Filtered Messages: none"),
mainWidget );
QVBoxLayout *lowerBoxLayout = new QVBoxLayout( lowerBox );
QString checkBoxText(
(showLaterMsgs) ?
i18nc("@option:check", "Show messages matched by a ruleset and tagged 'Download' or 'Delete'") :
i18nc("@option:check", "Show messages matched by a filter ruleset") );
QCheckBox* cb = new QCheckBox( checkBoxText, lowerBox );
cb->setEnabled( false );
mFilteredHeaders = new KMPopHeadersView( lowerBox, this );
mFilteredHeaders->hide();
lowerBoxLayout->addWidget( cb );
lowerBoxLayout->addWidget( mFilteredHeaders );
mainLayout->addWidget( lowerBox );
// fill the listviews with data from the headers
for ( int i = 0; i < headers.count(); ++i ) {
KMPopHeaders *header = headers[i];
KMPopHeadersViewItem *lvi = 0;
if ( header->ruleMatched() ) {
if ( showLaterMsgs && header->action() == Later ) {
// insert messages tagged 'later' only
lvi = new KMPopHeadersViewItem( mFilteredHeaders, header->action() );
mFilteredHeaders->addTopLevelItem( lvi );
mFilteredHeaders->show();
mLowerBoxVisible = true;
}
else if ( showLaterMsgs ) {
// enable checkbox to show 'delete' and 'download' msgs
// but don't insert them into the listview yet
mDDLList.append( header );
cb->setEnabled( true );
}
else if ( !showLaterMsgs ) {
// insert all messaged tagged by a ruleset, enable
// the checkbox, but don't show the listview yet
lvi = new KMPopHeadersViewItem( mFilteredHeaders, header->action() );
mFilteredHeaders->addTopLevelItem( lvi );
cb->setEnabled( true );
}
rulesetCount++;
}
else {
// insert all messages not tagged by a ruleset
// into the upper listview
lvi = new KMPopHeadersViewItem( upperHeadersView, header->action() );
upperHeadersView->addTopLevelItem( lvi );
upperBox->show();
}
if ( lvi ) {
mItemMap[lvi] = header;
setupLVI( lvi, header->header() );
}
}
// Initally sort the columns of the treewidgets by size
upperHeadersView->slotSectionClicked( 7 );
mFilteredHeaders->slotSectionClicked( 7 );
if ( rulesetCount )
lowerBox->setTitle( i18nc("@title:group", "Ruleset Filtered Messages: %1", rulesetCount ) );
// connect signals and slots
connect( cb, SIGNAL( toggled(bool) ),
this, SLOT( slotToggled(bool) ) );
resize( 800, 600 );
setUpdatesEnabled( true );
}
KMPopFilterCnfrmDlg::~KMPopFilterCnfrmDlg()
{
}
void KMPopFilterCnfrmDlg::setupLVI( KMPopHeadersViewItem *lvi, KMMessage *msg )
{
// set the subject
QString tmp = msg->subject();
if( tmp.isEmpty() )
tmp = i18nc("@item:intext", "No Subject");
lvi->setText( 3, tmp );
lvi->setToolTip( 3, tmp );
// set the sender
tmp = msg->fromStrip();
if( tmp.isEmpty() )
tmp = i18nc("@item:intext Sender of message is unknown", "Unknown");
lvi->setText( 4, tmp );
lvi->setToolTip( 4, tmp );
// set the receiver
tmp = msg->toStrip();
if( tmp.isEmpty() )
tmp = i18nc("@item:intext Receiver of message is unknown", "Unknown");
lvi->setText( 5, tmp );
lvi->setToolTip( 5, tmp );
// set the date
lvi->setText( 6, KMime::DateFormatter::formatDate(
KMime::DateFormatter::Fancy, msg->date() ) );
lvi->setIsoDate( msg->dateIsoStr() );
// set the size
lvi->setText( 7, KIO::convertSize( msg->msgLength() ) );
lvi->setMessageSize( msg->msgLength() );
connect( lvi, SIGNAL( radioButtonClicked(QTreeWidgetItem*, int) ),
lvi->treeWidget(), SLOT( slotRadioButtonClicked(QTreeWidgetItem*,int) ) );
}
void KMPopFilterCnfrmDlg::setAction( QTreeWidgetItem *item,
KMPopFilterAction action )
{
mItemMap[item]->setAction( action );
}
void KMPopFilterCnfrmDlg::slotToggled( bool on )
{
setUpdatesEnabled( false );
if ( on ) {
if ( mShowLaterMsgs ) {
// show download and deletek msgs in the list view too
for ( int i = 0; i < mDDLList.count(); ++i ) {
KMPopHeaders *headers = mDDLList[i];
KMPopHeadersViewItem *lvi =
new KMPopHeadersViewItem( mFilteredHeaders, headers->action() );
mFilteredHeaders->addTopLevelItem( lvi );
mItemMap[lvi] = headers;
mDelList.append( lvi );
setupLVI( lvi, headers->header() );
}
}
if ( !mLowerBoxVisible ) {
mFilteredHeaders->show();
}
}
else {
if ( mShowLaterMsgs ) {
// delete download and delete msgs from the lower listview
for ( int i = 0; i < mDelList.count(); ++i ) {
delete mDelList[i];
}
mDelList.clear();
}
if ( !mLowerBoxVisible ) {
mFilteredHeaders->hide();
}
}
setUpdatesEnabled( true );
}
#include "kmpopfiltercnfrmdlg.moc"