/* * kmail: KDE mail client * Copyright (c) 1996-1998 Stefan Taferner * Copyright (c) 2001 Aaron J. Seigo * Copyright (c) 2010 Till Adam * * 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 "folderrequester.h" #include "kmcommands.h" #include "kmmainwidget.h" #include "mailcommon/mailkernel.h" #include "mailcommon/searchpatternedit.h" #include "regexplineedit.h" #include "searchdescriptionattribute.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KPIM; using namespace MailCommon; namespace KMail { const int SearchWindow::MSGID_COLUMN = 4; SearchWindow::SearchWindow( KMMainWidget *widget, const Akonadi::Collection &collection ) : KDialog( 0 ), mStopped( false ), mCloseRequested( false ), mSortColumn( 0 ), mSortOrder( Qt::AscendingOrder ), mSearchJob( 0 ), mTimer( new QTimer( this ) ), mResultModel( 0 ), mLastFocus( 0 ), mKMMainWidget( widget ) { setCaption( i18n( "Find Messages" ) ); setButtons( User1 | User2 | Close ); setDefaultButton( User1 ); setButtonGuiItem( User1, KGuiItem( i18nc( "@action:button Search for messages", "&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::self()->config(); QWidget *searchWidget = new QWidget( this ); QVBoxLayout *vbl = new QVBoxLayout( searchWidget ); 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(); mChkbxSpecificFolders = new QRadioButton( i18n( "Search &only in:" ), searchWidget ); mChkbxSpecificFolders->setChecked( true ); mCbxFolders = new FolderRequester( searchWidget ); mCbxFolders->setMustBeReadWrite( false ); mCbxFolders->setNotAllowToCreateNewFolder( true ); mCbxFolders->setFolder( collection ); 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(); QGroupBox *patternGroupBox = new QGroupBox( searchWidget ); QHBoxLayout *layout = new QHBoxLayout( patternGroupBox ); layout->setContentsMargins( 0, 0, 0, 0 ); mPatternEdit = new SearchPatternEdit( searchWidget, false, true ); layout->addWidget( mPatternEdit ); patternGroupBox->setFlat( true ); bool currentFolderIsSearchFolder = false; if ( !collection.hasAttribute() ) { // it's not a search folder, make a new search mSearchPattern.append( SearchRule::createInstance( "Subject" ) ); } else { // it's a search folder if ( collection.hasAttribute() ) { currentFolderIsSearchFolder = true; // FIXME is there a better way to tell? const Akonadi::SearchDescriptionAttribute* searchDescription = collection.attribute(); 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->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( const QPoint& ) ), this, SLOT( slotContextMenuRequested( const QPoint& ) ) ); connect( mLbxMatches, SIGNAL( clicked( const Akonadi::Item& ) ), this, SLOT( slotShowMsg( const Akonadi::Item& ) ) ); connect( mLbxMatches, SIGNAL( doubleClicked( const Akonadi::Item& ) ), this, SLOT( slotViewMsg( const Akonadi::Item& ) ) ); connect( mLbxMatches, SIGNAL( currentChanged( const Akonadi::Item& ) ), this, SLOT( slotCurrentChanged( const Akonadi::Item& ) ) ); QHBoxLayout *hbl2 = new QHBoxLayout(); mSearchFolderLbl = new QLabel( i18n( "Search folder &name:" ), searchWidget ); mSearchFolderEdt = new KLineEdit( searchWidget ); mSearchFolderEdt->setClearButtonShown( true ); if ( currentFolderIsSearchFolder ) { mFolder = collection; mSearchFolderEdt->setText( collection.name() ); Q_ASSERT ( !mResultModel ); mResultModel = new Akonadi::ItemModel( 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 = CommonKernel->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 ); const int mainWidth = GlobalSettings::self()->searchWidgetWidth(); const int mainHeight = GlobalSettings::self()->searchWidgetHeight(); if ( mainWidth || mainHeight ) resize( mainWidth, mainHeight ); setMainWidget( searchWidget ); setButtonsOrientation( Qt::Vertical ); enableButton( User2, false ); //Bring all the layouts together vbl->addWidget( radioFrame ); vbl->addWidget( patternGroupBox ); vbl->addWidget( mLbxMatches ); vbl->addLayout( hbl2 ); vbl->addWidget( mStatusBar ); patternGroupBox->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Maximum ); 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" ); 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 ) { GlobalSettings::self()->setSubjectWidth( mLbxMatches->columnWidth( 0 ) ); GlobalSettings::self()->setSenderWidth( mLbxMatches->columnWidth( 1 ) ); GlobalSettings::self()->setDateWidth( mLbxMatches->columnWidth( 2 ) ); GlobalSettings::self()->setFolderWidth( mLbxMatches->columnWidth( 3 ) ); GlobalSettings::self()->setSearchWidgetWidth( width() ); GlobalSettings::self()->setSearchWidgetHeight( height() ); GlobalSettings::self()->requestSync(); } } 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( 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 *event ) { if ( event->key() == Qt::Key_Escape && mSearchJob ) { slotStop(); return; } KDialog::keyPressEvent( event ); } void SearchWindow::slotFolderActivated() { mChkbxSpecificFolders->setChecked( true ); } void SearchWindow::activateFolder( const Akonadi::Collection &collection ) { mChkbxSpecificFolders->setChecked( true ); mCbxFolders->setFolder( collection ); } 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(); SearchPattern searchPattern( mSearchPattern ); searchPattern.purify(); enableGUI(); mTimer->start( 200 ); #ifdef AKONADI_USE_STRIGI_SEARCH const QString query = searchPattern.asXesamQuery(); const QString queryLanguage = "XESAM"; #else const QString query = searchPattern.asSparqlQuery(); const QString queryLanguage = "SPARQL"; #endif kDebug() << query; 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(), query, this ); } else { Akonadi::PersistentSearchAttribute *attribute = mFolder.attribute(); attribute->setQueryLanguage( queryLanguage ); attribute->setQueryString( query ); 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 *searchJob = qobject_cast( mSearchJob ) ) { mFolder = searchJob->createdCollection(); } else if ( Akonadi::CollectionModifyJob *modifyJob = qobject_cast( mSearchJob ) ) { mFolder = modifyJob->collection(); } /// TODO: cope better with cases where this fails Q_ASSERT( mFolder.isValid() ); Q_ASSERT( mFolder.hasAttribute() ); 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::Entity::AddIfMissing ); searchDescription->setDescription( search ); const Akonadi::Collection collection = mCbxFolders->folderCollection(); searchDescription->setBaseCollection( collection ); searchDescription->setRecursive( mChkSubFolders->isChecked() ); new Akonadi::CollectionModifyJob( mFolder, this ); mSearchJob = 0; if ( !mResultModel ) { mResultModel = new Akonadi::MessageModel( this ); mResultModel->setCollection( mFolder ); mLbxMatches->setModel( mResultModel ); mLbxMatches->setColumnWidth( 0, GlobalSettings::self()->subjectWidth() ); mLbxMatches->setColumnWidth( 1, GlobalSettings::self()->senderWidth() ); mLbxMatches->setColumnWidth( 2, GlobalSettings::self()->dateWidth() ); mLbxMatches->setColumnWidth( 3, GlobalSettings::self()->folderWidth() ); 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 *event ) { 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( event ); } } void SearchWindow::scheduleRename( const QString &text ) { if ( !text.isEmpty() ) { mRenameTimer.setSingleShot( true ); mRenameTimer.start( 250 ); mSearchFolderOpenBtn->setEnabled( false ); } else { mRenameTimer.stop(); mSearchFolderOpenBtn->setEnabled( !text.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->slotSelectCollectionFolder( mFolder ); slotClose(); } bool SearchWindow::slotShowMsg( const Akonadi::Item &item ) { if ( item.isValid() ) { mKMMainWidget->slotMessageSelected( item ); return true; } return false; } void SearchWindow::slotViewSelectedMsg() { mKMMainWidget->slotMessageActivated( selectedMessage() ); } 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 ); } Akonadi::Item::List SearchWindow::selectedMessages() const { Akonadi::Item::List messages; foreach ( const QModelIndex &index, mLbxMatches->selectionModel()->selectedRows() ) { const Akonadi::Item item = index.data( Akonadi::ItemModel::ItemRole ).value(); if ( item.isValid() ) messages.append( item ); } return messages; } Akonadi::Item SearchWindow::selectedMessage() const { return mLbxMatches->currentIndex().data( Akonadi::ItemModel::ItemRole ).value(); } void SearchWindow::updateContextMenuActions() { const int count = selectedMessages().count(); const bool singleActions = (count == 1); mReplyAction->setEnabled( singleActions ); mReplyAllAction->setEnabled( singleActions ); mReplyListAction->setEnabled( singleActions ); mPrintAction->setEnabled( singleActions ); } void SearchWindow::slotContextMenuRequested( const QPoint& ) { if ( !selectedMessage().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, selectedMessage() ); command->start(); } void SearchWindow::slotReplyAllToMsg() { KMCommand *command = new KMReplyToAllCommand( this, selectedMessage() ); command->start(); } void SearchWindow::slotReplyListToMsg() { KMCommand *command = new KMReplyListCommand( this, selectedMessage() ); 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, selectedMessage() ); command->start(); } void SearchWindow::addRulesToSearchPattern( const SearchPattern &pattern ) { SearchPattern p( mSearchPattern ); p.purify(); QList::const_iterator it; for ( it = pattern.begin() ; it != pattern.end() ; ++it ) { p.append( SearchRule::createInstance( **it ) ); } mSearchPattern = p; mPatternEdit->setSearchPattern( &mSearchPattern ); } void SearchWindow::setSearchPattern( const SearchPattern &pattern ) { mSearchPattern = pattern; mPatternEdit->setSearchPattern( &mSearchPattern ); } } #include "searchwindow.moc"