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.
 
 
 

855 lines
31 KiB

/*
* kmail: KDE mail client
* Copyright (c) 1996-1998 Stefan Taferner <taferner@kde.org>
* Copyright (c) 2001 Aaron J. Seigo <aseigo@kde.org>
* Copyright (c) 2010 Till Adam <adam@kde.org>
*
* 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 "searchwindow.h"
#include <QCheckBox>
#include <QCloseEvent>
#include <QCursor>
#include <QHeaderView>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QLabel>
#include <QMenu>
#include <QRadioButton>
#include <QVBoxLayout>
#include <KActionMenu>
#include <KConfigGroup>
#include <KDebug>
#include <KIcon>
#include <KIconLoader>
#include <KLineEdit>
#include <KPushButton>
#include <KSharedConfig>
#include <KStandardAction>
#include <KStandardGuiItem>
#include <KStatusBar>
#include <KWindowSystem>
#include <KMessageBox>
#include <akonadi/standardactionmanager.h>
#include "folderrequester.h"
#include "kmcommands.h"
#include "kmmainwidget.h"
#include "kmsearchpatternedit.h"
#include "kmsearchpattern.h"
#include "searchdescriptionattribute.h"
#include "regexplineedit.h"
using namespace KPIM;
#include <Akonadi/AttributeFactory>
#include <Akonadi/EntityTreeView>
#include <Akonadi/CollectionModifyJob>
#include <Akonadi/SearchCreateJob>
#include <Akonadi/KMime/MessageModel>
#include <akonadi/persistentsearchattribute.h>
#include <Akonadi/CollectionStatisticsJob>
#include <kmime/kmime_message.h>
#include <assert.h>
#include <stdlib.h>
namespace KMail {
const int SearchWindow::MSGID_COLUMN = 4;
//-----------------------------------------------------------------------------
SearchWindow::SearchWindow(KMMainWidget* w, const Akonadi::Collection& curFolder):
KDialog(0),
mStopped(false),
mCloseRequested(false),
mSortColumn(0),
mSortOrder(Qt::AscendingOrder),
mSearchJob( 0 ),
mTimer(new QTimer(this)),
mResultModel( 0 ),
mLastFocus(0),
mKMMainWidget(w)
{
setCaption( i18n("Find Messages") );
setButtons( User1 | User2 | Close );
setDefaultButton( User1 );
setButtonGuiItem( User1, KGuiItem( i18nc("@action:button Search for messags", "&Search"), "edit-find" ) );
setButtonGuiItem( User2, KStandardGuiItem::stop() );
KWindowSystem::setIcons( winId(), qApp->windowIcon().pixmap(
IconSize( KIconLoader::Desktop ),
IconSize( KIconLoader::Desktop ) ),
qApp->windowIcon().pixmap(
IconSize( KIconLoader::Small ),
IconSize( KIconLoader::Small ) ) );
KSharedConfig::Ptr config = KMKernel::config();
KConfigGroup group( config, "SearchDialog" );
QWidget* searchWidget = new QWidget(this);
QVBoxLayout *vbl = new QVBoxLayout( searchWidget );
vbl->setObjectName( "kmfs_vbl" );
vbl->setMargin( 0 );
QFrame * radioFrame = new QFrame( searchWidget );
QVBoxLayout * radioLayout = new QVBoxLayout( radioFrame );
mChkbxAllFolders = new QRadioButton(i18n("Search in &all local folders"), searchWidget );
QHBoxLayout *hbl = new QHBoxLayout();
hbl->setObjectName( "kmfs_hbl" );
mChkbxSpecificFolders = new QRadioButton( i18n("Search &only in:"), searchWidget );
mChkbxSpecificFolders->setChecked(true);
mCbxFolders = new FolderRequester( searchWidget );
mCbxFolders->setMustBeReadWrite( false );
mCbxFolders->setNotAllowToCreateNewFolder( true );
mCbxFolders->setFolder(curFolder);
mChkSubFolders = new QCheckBox(i18n("I&nclude sub-folders"), searchWidget);
mChkSubFolders->setChecked(true);
radioLayout->addWidget( mChkbxAllFolders );
hbl->addWidget(mChkbxSpecificFolders);
hbl->addWidget(mCbxFolders);
hbl->addWidget(mChkSubFolders);
radioLayout->addLayout( hbl );
mChkbxSpecificFolders->hide();
mChkSubFolders->hide();
mCbxFolders->hide();
mChkbxAllFolders->hide();
mPatternEdit = new KMSearchPatternEdit( QString(), searchWidget, false, true );
mPatternEdit->setFlat( true );
bool currentFolderIsSearchFolder = false;
if ( !curFolder.hasAttribute<Akonadi::PersistentSearchAttribute>() ) {
// it's not a search folder, make a new search
mSearchPattern.append( KMSearchRule::createInstance( "Subject" ) );
} else {
// it's a search folder
if ( curFolder.hasAttribute<Akonadi::SearchDescriptionAttribute>() ) {
currentFolderIsSearchFolder = true; // FIXME is there a better way to tell?
const Akonadi::SearchDescriptionAttribute* searchDescription = curFolder.attribute<Akonadi::SearchDescriptionAttribute>();
mSearchPattern.deserialize( searchDescription->description() );
const Akonadi::Collection col = searchDescription->baseCollection();
if ( col.isValid() ) {
mChkbxSpecificFolders->setChecked( true );
mCbxFolders->setFolder( col );
mChkSubFolders->setChecked( searchDescription->recursive() );
} else {
mChkbxAllFolders->setChecked( true );
}
} else {
// it's a search folder, but not one of ours, warn the user that we can't edit it
// FIXME show results, but disable edit GUI
kWarning() << "This search was not created with KMail. It can not be edited within it.";
mSearchPattern.clear();
}
}
mPatternEdit->setSearchPattern( &mSearchPattern );
// enable/disable widgets depending on radio buttons:
connect( mChkbxSpecificFolders, SIGNAL(toggled(bool)),
mCbxFolders, SLOT(setEnabled(bool)) );
connect( mChkbxSpecificFolders, SIGNAL(toggled(bool)),
mChkSubFolders, SLOT(setEnabled(bool)) );
connect( mChkbxAllFolders, SIGNAL(toggled(bool)),
this, SLOT(setEnabledSearchButton(bool)) );
mLbxMatches = new Akonadi::EntityTreeView( mKMMainWidget->guiClient(), this );
mLbxMatches->setObjectName( "Find Messages" );
mLbxMatches->setAlternatingRowColors( true );
/*
Default is to sort by date. TODO: Unfortunately this sorts *while*
inserting, which looks rather strange - the user cannot read
the results so far as they are constantly re-sorted --dnaber
Sorting is now disabled when a search is started and reenabled
when it stops. Items are appended to the list. This not only
solves the above problem, but speeds searches with many hits
up considerably. - till
TODO: subclass QTreeWidgetItem and do proper (and performant)
compare functions
*/
mLbxMatches->setSortingEnabled( true );
#if 0 // port me!
mLbxMatches->sortItems( 2, Qt::DescendingOrder );
#else
kDebug() << "AKONADI PORT: Disabled code in " << Q_FUNC_INFO;
#endif
mLbxMatches->setAllColumnsShowFocus( true );
mLbxMatches->setSelectionMode( QAbstractItemView::ExtendedSelection );
mLbxMatches->setContextMenuPolicy( Qt::CustomContextMenu );
connect( mLbxMatches, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(slotContextMenuRequested(QPoint)) );
connect( mLbxMatches, SIGNAL(clicked(Akonadi::Item)), SLOT(slotShowMsg(Akonadi::Item)) );
connect( mLbxMatches, SIGNAL(doubleClicked(Akonadi::Item)), SLOT(slotViewMsg(Akonadi::Item)) );
connect( mLbxMatches, SIGNAL(currentChanged(Akonadi::Item)), SLOT(slotCurrentChanged(Akonadi::Item)) );
QHBoxLayout *hbl2 = new QHBoxLayout();
hbl2->setObjectName( "kmfs_hbl2" );
mSearchFolderLbl = new QLabel( i18n("Search folder &name:"), searchWidget );
mSearchFolderEdt = new KLineEdit( searchWidget );
mSearchFolderEdt->setClearButtonShown( true );
if ( currentFolderIsSearchFolder ) {
mFolder = curFolder;
mSearchFolderEdt->setText( curFolder.name() );
Q_ASSERT ( !mResultModel );
mResultModel = new Akonadi::MessageModel( this );
mResultModel->setCollection( mFolder );
mLbxMatches->setModel( mResultModel );
mAkonadiStandardAction = new Akonadi::StandardActionManager( actionCollection(), this );
mAkonadiStandardAction->setItemSelectionModel( mLbxMatches->selectionModel() );
} else {
mSearchFolderEdt->setText( i18n("Last Search") );
// find last search and reuse it if possible
mFolder = KMKernel::self()->collectionFromId( GlobalSettings::lastSearchCollectionId() );
// when the last folder got renamed, create a new one
if ( mFolder.isValid() && mFolder.name() != mSearchFolderEdt->text() ) {
mFolder = Akonadi::Collection();
}
}
mSearchFolderLbl->setBuddy(mSearchFolderEdt);
mSearchFolderOpenBtn = new KPushButton(i18n("Op&en Search Folder"), searchWidget);
mSearchFolderOpenBtn->setEnabled(false);
connect( mSearchFolderEdt, SIGNAL( textChanged( const QString &)),
this, SLOT( scheduleRename( const QString & )));
connect( &mRenameTimer, SIGNAL( timeout() ),
this, SLOT( renameSearchFolder() ));
connect( mSearchFolderOpenBtn, SIGNAL( clicked() ),
this, SLOT( openSearchFolder() ));
mSearchResultOpenBtn = new KPushButton( i18n("Open &Message"), searchWidget );
mSearchResultOpenBtn->setEnabled( false );
connect( mSearchResultOpenBtn, SIGNAL( clicked() ),
this, SLOT( slotViewSelectedMsg() ) );
hbl2->addWidget(mSearchFolderLbl);
hbl2->addWidget(mSearchFolderEdt);
hbl2->addWidget(mSearchFolderOpenBtn);
hbl2->addWidget(mSearchResultOpenBtn);
mStatusBar = new KStatusBar(searchWidget);
mStatusBar->insertPermanentItem(i18n("AMiddleLengthText..."), 0);
mStatusBar->changeItem(i18nc("@info:status finished searching.", "Ready."), 0);
mStatusBar->setItemAlignment(0, Qt::AlignLeft | Qt::AlignVCenter);
mStatusBar->insertPermanentItem(QString(), 1, 1);
mStatusBar->setItemAlignment(1, Qt::AlignLeft | Qt::AlignVCenter);
int mainWidth = group.readEntry("SearchWidgetWidth", 0 );
int mainHeight = group.readEntry("SearchWidgetHeight", 0 );
if (mainWidth || mainHeight)
resize(mainWidth, mainHeight);
setMainWidget(searchWidget);
setButtonsOrientation(Qt::Vertical);
enableButton(User2, false);
//Bring all the layouts together
vbl->addWidget( radioFrame );
vbl->addWidget( mPatternEdit );
vbl->addWidget( mLbxMatches );
vbl->addLayout( hbl2 );
vbl->addWidget( mStatusBar );
connect(this, SIGNAL(user1Clicked()), SLOT(slotSearch()));
connect(this, SIGNAL(user2Clicked()), SLOT(slotStop()));
connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));
connect(this,SIGNAL(closeClicked()),this,SLOT(slotClose()));
// give focus to the value field of the first search rule
RegExpLineEdit* r = mPatternEdit->findChild<RegExpLineEdit*>( "regExpLineEdit" );
if ( r )
r->setFocus();
else
kDebug() << "SearchWindow: regExpLineEdit not found";
//set up actions
KActionCollection *ac = actionCollection();
mReplyAction = new KAction(KIcon("mail-reply-sender"), i18n("&Reply..."), this);
actionCollection()->addAction("search_reply", mReplyAction );
connect(mReplyAction, SIGNAL(triggered(bool)), SLOT(slotReplyToMsg()));
mReplyAllAction = new KAction(KIcon("mail-reply-all"), i18n("Reply to &All..."), this);
actionCollection()->addAction("search_reply_all", mReplyAllAction );
connect(mReplyAllAction, SIGNAL(triggered(bool) ), SLOT(slotReplyAllToMsg()));
mReplyListAction = new KAction(KIcon("mail-reply-list"), i18n("Reply to Mailing-&List..."), this);
actionCollection()->addAction("search_reply_list", mReplyListAction );
connect(mReplyListAction, SIGNAL(triggered(bool) ), SLOT(slotReplyListToMsg()));
mForwardActionMenu = new KActionMenu(KIcon("mail-forward"), i18nc("Message->","&Forward"), this);
actionCollection()->addAction("search_message_forward", mForwardActionMenu );
connect( mForwardActionMenu, SIGNAL(triggered(bool)), this,
SLOT(slotForwardMsg()) );
mForwardInlineAction = new KAction( KIcon( "mail-forward" ),
i18nc( "@action:inmenu Forward message inline.",
"&Inline..." ),
this );
actionCollection()->addAction("search_message_forward_inline", mForwardInlineAction );
connect(mForwardInlineAction, SIGNAL(triggered(bool) ), SLOT(slotForwardMsg()));
mForwardAttachedAction = new KAction(KIcon("mail-forward"), i18nc("Message->Forward->","As &Attachment..."), this);
actionCollection()->addAction("search_message_forward_as_attachment", mForwardAttachedAction );
connect(mForwardAttachedAction, SIGNAL(triggered(bool)), SLOT(slotForwardAttachedMsg()));
if ( GlobalSettings::self()->forwardingInlineByDefault() ) {
mForwardActionMenu->addAction( mForwardInlineAction );
mForwardActionMenu->addAction( mForwardAttachedAction );
}
else {
mForwardActionMenu->addAction( mForwardAttachedAction );
mForwardActionMenu->addAction( mForwardInlineAction );
}
mSaveAsAction = actionCollection()->addAction(KStandardAction::SaveAs, "search_file_save_as", this, SLOT(slotSaveMsg()));
mSaveAtchAction = new KAction(KIcon("mail-attachment"), i18n("Save Attachments..."), this);
actionCollection()->addAction("search_save_attachments", mSaveAtchAction );
connect(mSaveAtchAction, SIGNAL(triggered(bool)), SLOT(slotSaveAttachments()));
mPrintAction = actionCollection()->addAction(KStandardAction::Print, "search_print", this, SLOT(slotPrintMsg()));
mClearAction = new KAction(i18n("Clear Selection"), this);
actionCollection()->addAction("search_clear_selection", mClearAction );
connect(mClearAction, SIGNAL(triggered(bool)), SLOT(slotClearSelection()));
connect(mTimer, SIGNAL(timeout()), this, SLOT(updateStatusLine()));
connect(mCbxFolders, SIGNAL(folderChanged(const Akonadi::Collection&)),
this, SLOT(slotFolderActivated()));
ac->addAssociatedWidget( this );
foreach (QAction* action, ac->actions())
action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
}
//-----------------------------------------------------------------------------
SearchWindow::~SearchWindow()
{
if ( mResultModel ) {
KSharedConfig::Ptr config = KMKernel::config();
KConfigGroup group( config, "SearchDialog" );
group.writeEntry( "SubjectWidth", mLbxMatches->columnWidth( 0 ) );
group.writeEntry( "SenderWidth", mLbxMatches->columnWidth( 1 ) );
group.writeEntry( "DateWidth", mLbxMatches->columnWidth( 2 ) );
group.writeEntry( "FolderWidth", mLbxMatches->columnWidth( 3 ) );
group.writeEntry( "SearchWidgetWidth", width());
group.writeEntry( "SearchWidgetHeight", height());
config->sync();
}
}
void SearchWindow::setEnabledSearchButton( bool )
{
//Make sure that button is enable
//Before when we selected a folder == "Local Folder" as that it was not a folder
//search button was disable, and when we select "Search in all local folder"
//Search button was never enabled :(
enableButton( User1, true );
}
//-----------------------------------------------------------------------------
void SearchWindow::updateStatusLine()
{
if ( mFolder.isValid() ) {
Akonadi::CollectionStatisticsJob *job = new Akonadi::CollectionStatisticsJob( mFolder );
connect( job, SIGNAL( result( KJob* ) ), SLOT( updateCollectionStatisticsFinished( KJob* ) ) );
}
}
void SearchWindow::updateCollectionStatisticsFinished( KJob * job)
{
if ( job->error() )
kWarning() << job->errorText(); // TODO
else {
QString genMsg, detailMsg;
int numMatches = 0;
Akonadi::CollectionStatisticsJob *statisticsJob = qobject_cast<Akonadi::CollectionStatisticsJob*>( job );
const Akonadi::CollectionStatistics statistics = statisticsJob->statistics();
numMatches = statistics.count();
if ( mFolder.isValid() && mSearchJob ) {
if(!mStopped) {
genMsg = i18nc( "Search finished.", "Done" );
detailMsg = i18np( "%1 match", "%1 matches", numMatches );
} else {
genMsg = i18n( "Search canceled" );
detailMsg = i18np( "%1 match so far",
"%1 matches so far", numMatches );
}
} else {
genMsg = i18np( "%1 match", "%1 matches", numMatches );
detailMsg = i18n( "Searching in %1", mFolder.name() );
}
mStatusBar->changeItem(genMsg, 0);
mStatusBar->changeItem(detailMsg, 1);
}
}
//-----------------------------------------------------------------------------
void SearchWindow::keyPressEvent(QKeyEvent *evt)
{
if (evt->key() == Qt::Key_Escape && mSearchJob) {
slotStop();
return;
}
KDialog::keyPressEvent(evt);
}
//-----------------------------------------------------------------------------
void SearchWindow::slotFolderActivated()
{
mChkbxSpecificFolders->setChecked(true);
}
//-----------------------------------------------------------------------------
void SearchWindow::activateFolder(const Akonadi::Collection& curFolder)
{
mChkbxSpecificFolders->setChecked(true);
mCbxFolders->setFolder(curFolder);
}
//-----------------------------------------------------------------------------
void SearchWindow::slotSearch()
{
mLastFocus = focusWidget();
setButtonFocus( User1 ); // set focus so we don't miss key event
mStopped = false;
mSearchFolderOpenBtn->setEnabled( true );
if ( mSearchFolderEdt->text().isEmpty() ) {
mSearchFolderEdt->setText( i18n("Last Search") );
}
enableButton( User1, false );
enableButton( User2, true );
if ( mResultModel )
mHeaderState = mLbxMatches->header()->saveState();
mLbxMatches->setModel( 0 );
mSortColumn = mLbxMatches->header()->sortIndicatorSection();
mSortOrder = mLbxMatches->header()->sortIndicatorOrder();
mLbxMatches->setSortingEnabled( false );
if ( mSearchJob ) {
mSearchJob->kill( KJob::Quietly );
mSearchJob->deleteLater();
mSearchJob = 0;
}
mSearchFolderEdt->setEnabled( false );
#if 0
if ( mChkbxAllFolders->isChecked() ) {
search->setRecursive( true );
} else {
search->setRoot( mCbxFolders->folder() );
search->setRecursive( mChkSubFolders->isChecked() );
}
#else
kDebug() << "AKONADI PORT: Disabled code in " << Q_FUNC_INFO;
#endif
mPatternEdit->updateSearchPattern();
KMSearchPattern searchPattern( mSearchPattern );
searchPattern.purify();
enableGUI();
mTimer->start( 200 );
kDebug() << searchPattern.asSparqlQuery();
if ( !mFolder.isValid() ) {
// FIXME if another app created a virtual 'Last Search' folder without
// out custom attributes it will result in problems
mSearchJob = new Akonadi::SearchCreateJob( mSearchFolderEdt->text(), searchPattern.asSparqlQuery(), this );
} else {
Akonadi::PersistentSearchAttribute *psa = mFolder.attribute<Akonadi::PersistentSearchAttribute>();
psa->setQueryString( searchPattern.asSparqlQuery() );
mSearchJob = new Akonadi::CollectionModifyJob( mFolder, this );
}
connect( mSearchJob, SIGNAL(result(KJob*)), SLOT(searchDone(KJob*)) );
}
//-----------------------------------------------------------------------------
void SearchWindow::searchDone( KJob* job )
{
Q_ASSERT( job == mSearchJob );
if ( job->error() )
kWarning() << job->errorText(); // TODO
if ( Akonadi::SearchCreateJob *scj = qobject_cast<Akonadi::SearchCreateJob*>( mSearchJob ) ) {
mFolder = scj->createdCollection();
} else if ( Akonadi::CollectionModifyJob *mj = qobject_cast<Akonadi::CollectionModifyJob*>( mSearchJob ) ) {
mFolder = mj->collection();
}
/// TODO: cope better with cases where this fails
Q_ASSERT( mFolder.isValid() );
Q_ASSERT( mFolder.hasAttribute<Akonadi::PersistentSearchAttribute>() );
GlobalSettings::setLastSearchCollectionId( mFolder.id() );
GlobalSettings::self()->writeConfig();
GlobalSettings::self()->requestSync();
// store the kmail specific serialization of the search in an attribute on
// the server, for easy retrieval when editing it again
const QByteArray search = mSearchPattern.serialize();
Q_ASSERT( !search.isEmpty() );
Akonadi::SearchDescriptionAttribute *searchDescription = mFolder.attribute<Akonadi::SearchDescriptionAttribute>( Akonadi::Entity::AddIfMissing );
searchDescription->setDescription( search );
const Akonadi::Collection col = mCbxFolders->folderCollection();
searchDescription->setBaseCollection( col );
searchDescription->setRecursive( mChkSubFolders->isChecked() );
new Akonadi::CollectionModifyJob( mFolder, this );
mSearchJob = 0;
if ( !mResultModel ) {
mResultModel = new Akonadi::MessageModel( this );
mResultModel->setCollection( mFolder );
mLbxMatches->setModel( mResultModel );
KSharedConfig::Ptr config = KMKernel::config();
KConfigGroup group( config, "SearchDialog" );
mLbxMatches->setColumnWidth( 0, group.readEntry( "SubjectWidth", 150 ) );
mLbxMatches->setColumnWidth( 1, group.readEntry( "SenderWidth", 120 ) );
mLbxMatches->setColumnWidth( 2, group.readEntry( "DateWidth", 120 ) );
mLbxMatches->setColumnWidth( 3, group.readEntry( "FolderWidth", 100 ) );
mLbxMatches->setColumnWidth( 4, 0 );
mLbxMatches->header()->setSortIndicator( 2, Qt::DescendingOrder );
mLbxMatches->header()->setStretchLastSection( false );
mLbxMatches->header()->setResizeMode( 3, QHeaderView::Stretch );
mAkonadiStandardAction = new Akonadi::StandardActionManager( actionCollection(), this );
mAkonadiStandardAction->setItemSelectionModel( mLbxMatches->selectionModel() );
} else {
mResultModel->setCollection( mFolder );
mLbxMatches->setModel( mResultModel );
mLbxMatches->header()->restoreState( mHeaderState );
}
mTimer->stop();
updateStatusLine();
QTimer::singleShot(0, this, SLOT(enableGUI()));
if(mLastFocus)
mLastFocus->setFocus();
if (mCloseRequested)
close();
mLbxMatches->setSortingEnabled( true );
mLbxMatches->header()->setSortIndicator( mSortColumn, mSortOrder );
mSearchFolderEdt->setEnabled( true );
}
//-----------------------------------------------------------------------------
void SearchWindow::slotStop()
{
if ( mSearchJob ) {
mSearchJob->kill( KJob::Quietly );
mSearchJob->deleteLater();
mSearchJob = 0;
}
mStopped = true;
enableButton( User2, false );
}
//-----------------------------------------------------------------------------
void SearchWindow::slotClose()
{
accept();
}
//-----------------------------------------------------------------------------
void SearchWindow::closeEvent(QCloseEvent *e)
{
if ( mSearchJob ) {
mCloseRequested = true;
//Cancel search in progress
mSearchJob->kill( KJob::Quietly );
mSearchJob->deleteLater();
mSearchJob = 0;
QTimer::singleShot( 0, this, SLOT( slotClose() ) );
} else {
KDialog::closeEvent( e );
}
}
//-----------------------------------------------------------------------------
void SearchWindow::scheduleRename( const QString &s )
{
if ( !s.isEmpty() ) {
mRenameTimer.setSingleShot( true );
mRenameTimer.start( 250 );
mSearchFolderOpenBtn->setEnabled( false );
} else {
mRenameTimer.stop();
mSearchFolderOpenBtn->setEnabled( !s.isEmpty() );
}
}
//-----------------------------------------------------------------------------
void SearchWindow::renameSearchFolder()
{
if ( mFolder.isValid() && ( mFolder.name() != mSearchFolderEdt->text() ) ) {
mFolder.setName( mSearchFolderEdt->text() );
Akonadi::CollectionModifyJob *job = new Akonadi::CollectionModifyJob( mFolder, this );
connect( job, SIGNAL( result( KJob* ) ),
this, SLOT( slotSearchFolderRenameDone( KJob* ) ) );
}
if ( mFolder.isValid() )
mSearchFolderOpenBtn->setEnabled( true );
}
void SearchWindow::slotSearchFolderRenameDone( KJob *job )
{
Q_ASSERT( job );
if ( job->error() ) {
kWarning() << "Job failed:" << job->errorText();
KMessageBox::information( this, i18n("There was a problem renaming your search folder. "
"A common reason for this is that another search folder "
"with the same name already exists.") );
} else {
kDebug() << "Search Collection succesfully renamed.";
}
}
void SearchWindow::openSearchFolder()
{
Q_ASSERT( mFolder.isValid() );
renameSearchFolder();
mKMMainWidget->selectCollectionFolder( mFolder );
slotClose();
}
//-----------------------------------------------------------------------------
bool SearchWindow::slotShowMsg( const Akonadi::Item &item )
{
if ( !item.isValid() )
return false;
mKMMainWidget->slotMessageSelected( item );
return true;
}
//-----------------------------------------------------------------------------
void SearchWindow::slotViewSelectedMsg()
{
mKMMainWidget->slotMessageActivated( message() );
}
//-----------------------------------------------------------------------------
bool SearchWindow::slotViewMsg( const Akonadi::Item &item )
{
if ( item.isValid() ) {
mKMMainWidget->slotMessageActivated( item );
return true;
}
return false;
}
//-----------------------------------------------------------------------------
void SearchWindow::slotCurrentChanged( const Akonadi::Item &item )
{
mSearchResultOpenBtn->setEnabled( item.isValid() );
}
//-----------------------------------------------------------------------------
void SearchWindow::enableGUI()
{
const bool searching = mSearchJob != 0;
enableButton(KDialog::Close, !searching);
mCbxFolders->setEnabled(!searching && !mChkbxAllFolders->isChecked());
mChkSubFolders->setEnabled(!searching && !mChkbxAllFolders->isChecked());
mChkbxAllFolders->setEnabled(!searching);
mChkbxSpecificFolders->setEnabled(!searching);
mPatternEdit->setEnabled(!searching);
enableButton(User1, !searching);
enableButton(User2, searching);
}
//-----------------------------------------------------------------------------
QList<Akonadi::Item> SearchWindow::selectedMessages()
{
QList<Akonadi::Item> msgList;
foreach ( const QModelIndex &index, mLbxMatches->selectionModel()->selectedRows() ) {
const Akonadi::Item item = index.data( Akonadi::MessageModel::ItemRole ).value<Akonadi::Item>();
if ( item.isValid() )
msgList.append( item );
}
return msgList;
}
//-----------------------------------------------------------------------------
Akonadi::Item SearchWindow::message()
{
return mLbxMatches->currentIndex().data( Akonadi::ItemModel::ItemRole ).value<Akonadi::Item>();
}
//-----------------------------------------------------------------------------
void SearchWindow::updateContextMenuActions()
{
const int count = selectedMessages().count();
bool single_actions = count == 1;
mReplyAction->setEnabled( single_actions );
mReplyAllAction->setEnabled( single_actions );
mReplyListAction->setEnabled( single_actions );
mPrintAction->setEnabled( single_actions );
}
//-----------------------------------------------------------------------------
void SearchWindow::slotContextMenuRequested( const QPoint &pos )
{
if (!message().isValid())
return;
QMenu *menu = new QMenu(this);
updateContextMenuActions();
// show most used actions
menu->addAction( mReplyAction );
menu->addAction( mReplyAllAction );
menu->addAction( mReplyListAction );
menu->addAction( mForwardActionMenu );
menu->addSeparator();
KAction *act = mAkonadiStandardAction->createAction( Akonadi::StandardActionManager::CopyItems );
mAkonadiStandardAction->setActionText( Akonadi::StandardActionManager::CopyItems, ki18np( "Copy Message", "Copy %1 Messages" ) );
menu->addAction( act );
act = mAkonadiStandardAction->createAction( Akonadi::StandardActionManager::CutItems );
mAkonadiStandardAction->setActionText( Akonadi::StandardActionManager::CutItems, ki18np( "Cut Message", "Cut %1 Messages" ) );
menu->addAction( act );
menu->addAction( mAkonadiStandardAction->createAction( Akonadi::StandardActionManager::CopyItemToMenu ) );
menu->addAction( mAkonadiStandardAction->createAction( Akonadi::StandardActionManager::MoveItemToMenu ) );
menu->addSeparator();
menu->addAction( mSaveAsAction );
menu->addAction( mSaveAtchAction );
menu->addAction( mPrintAction );
menu->addSeparator();
menu->addAction( mClearAction );
menu->exec( QCursor::pos(), 0 );
delete menu;
}
//-----------------------------------------------------------------------------
void SearchWindow::slotClearSelection()
{
mLbxMatches->clearSelection();
}
//-----------------------------------------------------------------------------
void SearchWindow::slotReplyToMsg()
{
KMCommand *command = new KMReplyToCommand(this, message());
command->start();
}
//-----------------------------------------------------------------------------
void SearchWindow::slotReplyAllToMsg()
{
KMCommand *command = new KMReplyToAllCommand(this, message());
command->start();
}
//-----------------------------------------------------------------------------
void SearchWindow::slotReplyListToMsg()
{
KMCommand *command = new KMReplyListCommand(this, message());
command->start();
}
//-----------------------------------------------------------------------------
void SearchWindow::slotForwardMsg()
{
KMCommand *command = new KMForwardCommand(this, selectedMessages());
command->start();
}
//-----------------------------------------------------------------------------
void SearchWindow::slotForwardAttachedMsg()
{
KMCommand *command = new KMForwardAttachedCommand(this, selectedMessages());
command->start();
}
//-----------------------------------------------------------------------------
void SearchWindow::slotSaveMsg()
{
KMSaveMsgCommand *saveCommand = new KMSaveMsgCommand(this,
selectedMessages());
if (saveCommand->url().isEmpty())
delete saveCommand;
else
saveCommand->start();
}
//-----------------------------------------------------------------------------
void SearchWindow::slotSaveAttachments()
{
KMSaveAttachmentsCommand *saveCommand = new KMSaveAttachmentsCommand(this,
selectedMessages());
saveCommand->start();
}
//-----------------------------------------------------------------------------
void SearchWindow::slotPrintMsg()
{
KMCommand *command = new KMPrintCommand(this, message());
command->start();
}
void SearchWindow::addRulesToSearchPattern( const KMSearchPattern &pattern )
{
KMSearchPattern p( mSearchPattern );
p.purify();
QList<KMSearchRule::Ptr>::const_iterator it;
for ( it = pattern.begin() ; it != pattern.end() ; ++it ) {
p.append( KMSearchRule::createInstance( **it ) );
}
mSearchPattern = p;
mPatternEdit->setSearchPattern( &mSearchPattern );
}
void SearchWindow::setSearchPattern( const KMSearchPattern &pattern )
{
mSearchPattern = pattern;
mPatternEdit->setSearchPattern( &mSearchPattern );
}
} // namespace KMail
#include "searchwindow.moc"