kmail

searchwindow.cpp

00001 /*
00002  * kmail: KDE mail client
00003  * Copyright (c) 1996-1998 Stefan Taferner <taferner@kde.org>
00004  * Copyright (c) 2001 Aaron J. Seigo <aseigo@kde.org>
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  *
00020  */
00021 #include <config.h>
00022 #include "kmcommands.h"
00023 #include "searchwindow.h"
00024 #include "kmmainwidget.h"
00025 #include "kmmsgdict.h"
00026 #include "kmmsgpart.h"
00027 #include "kmfolderimap.h"
00028 #include "kmfoldermgr.h"
00029 #include "kmfoldersearch.h"
00030 #include "kmfoldertree.h"
00031 #include "kmheaders.h"
00032 #include "kmsearchpatternedit.h"
00033 #include "kmsearchpattern.h"
00034 #include "folderrequester.h"
00035 #include "messagecopyhelper.h"
00036 #include "textsource.h"
00037 
00038 #include <kapplication.h>
00039 #include <kdebug.h>
00040 #include <kstatusbar.h>
00041 #include <kwin.h>
00042 #include <kconfig.h>
00043 #include <kstdaction.h>
00044 #include <kiconloader.h>
00045 
00046 #include <qcheckbox.h>
00047 #include <qlayout.h>
00048 #include <klineedit.h>
00049 #include <qpushbutton.h>
00050 #include <qradiobutton.h>
00051 #include <qbuttongroup.h>
00052 #include <qcombobox.h>
00053 #include <qobjectlist.h> //for mPatternEdit->queryList( 0, "mRuleField" )->first();
00054 #include <qcursor.h>
00055 #include <qpopupmenu.h>
00056 
00057 #include <maillistdrag.h>
00058 using namespace KPIM;
00059 
00060 #include <mimelib/enum.h>
00061 #include <mimelib/boyermor.h>
00062 
00063 #include <assert.h>
00064 #include <stdlib.h>
00065 
00066 namespace KMail {
00067 
00068 const int SearchWindow::MSGID_COLUMN = 4;
00069 
00070 // KListView sub-class for dnd support
00071 class MatchListView : public KListView
00072 {
00073   public:
00074     MatchListView( QWidget *parent, SearchWindow* sw, const char* name = 0 ) :
00075       KListView( parent, name ),
00076       mSearchWindow( sw )
00077     {}
00078 
00079   protected:
00080     virtual QDragObject* dragObject()
00081     {
00082       KMMessageList list = mSearchWindow->selectedMessages();
00083       MailList mailList;
00084       for ( KMMsgBase* msg = list.first(); msg; msg = list.next() ) {
00085         if ( !msg )
00086           continue;
00087         MailSummary mailSummary( msg->getMsgSerNum(), msg->msgIdMD5(),
00088                                  msg->subject(), msg->fromStrip(),
00089                                  msg->toStrip(), msg->date() );
00090         mailList.append( mailSummary );
00091       }
00092       MailListDrag *d = new MailListDrag( mailList, viewport(), new KMTextSource );
00093 
00094       QPixmap pixmap;
00095       if( mailList.count() == 1 )
00096         pixmap = QPixmap( DesktopIcon("message", KIcon::SizeSmall) );
00097       else
00098         pixmap = QPixmap( DesktopIcon("kmultiple", KIcon::SizeSmall) );
00099 
00100       d->setPixmap( pixmap );
00101       return d;
00102     }
00103 
00104   private:
00105     SearchWindow* mSearchWindow;
00106 };
00107 
00108 //-----------------------------------------------------------------------------
00109 SearchWindow::SearchWindow(KMMainWidget* w, const char* name,
00110                          KMFolder *curFolder, bool modal):
00111   KDialogBase(0, name, modal, i18n("Find Messages"),
00112               User1 | User2 | Close, User1, false,
00113               KGuiItem( i18n("&Search"), "find" ),
00114               KStdGuiItem::stop()),
00115   mStopped(false),
00116   mCloseRequested(false),
00117   mSortColumn(0),
00118   mSortOrder(Ascending),
00119   mFolder(0),
00120   mTimer(new QTimer(this, "mTimer")),
00121   mLastFocus(0),
00122   mKMMainWidget(w)
00123 {
00124 #if !KDE_IS_VERSION( 3, 2, 91 )
00125   // HACK - KWin keeps all dialogs on top of their mainwindows, but that's probably
00126   // wrong (#76026), and should be done only for modals. CVS HEAD should get
00127   // proper fix in KWin (l.lunak@kde.org)
00128   XDeleteProperty( qt_xdisplay(), winId(), XA_WM_TRANSIENT_FOR );
00129 #endif
00130   KWin::setIcons(winId(), kapp->icon(), kapp->miniIcon());
00131 
00132   KConfig* config = KMKernel::config();
00133   config->setGroup("SearchDialog");
00134 
00135   QWidget* searchWidget = new QWidget(this);
00136   QVBoxLayout *vbl = new QVBoxLayout( searchWidget, 0, spacingHint(), "kmfs_vbl" );
00137 
00138   QButtonGroup * radioGroup = new QButtonGroup( searchWidget );
00139   radioGroup->hide();
00140 
00141   mChkbxAllFolders = new QRadioButton(i18n("Search in &all local folders"), searchWidget);
00142   vbl->addWidget( mChkbxAllFolders );
00143   radioGroup->insert( mChkbxAllFolders );
00144 
00145   QHBoxLayout *hbl = new QHBoxLayout( vbl, spacingHint(), "kmfs_hbl" );
00146   mChkbxSpecificFolders = new QRadioButton(i18n("Search &only in:"), searchWidget);
00147   hbl->addWidget(mChkbxSpecificFolders);
00148   mChkbxSpecificFolders->setChecked(true);
00149   radioGroup->insert( mChkbxSpecificFolders );
00150 
00151   mCbxFolders = new FolderRequester( searchWidget,
00152       kmkernel->getKMMainWidget()->folderTree() );
00153   mCbxFolders->setMustBeReadWrite( false );
00154   mCbxFolders->setFolder(curFolder);
00155   hbl->addWidget(mCbxFolders);
00156 
00157   mChkSubFolders = new QCheckBox(i18n("I&nclude sub-folders"), searchWidget);
00158   mChkSubFolders->setChecked(true);
00159   hbl->addWidget(mChkSubFolders);
00160 
00161   QWidget *spacer = new QWidget( searchWidget, "spacer" );
00162   spacer->setMinimumHeight( 2 );
00163   vbl->addWidget( spacer );
00164 
00165   mPatternEdit = new KMSearchPatternEdit( "", searchWidget , "spe", false, true );
00166   mPatternEdit->setFrameStyle( QFrame::NoFrame | QFrame::Plain );
00167   mPatternEdit->setInsideMargin( 0 );
00168   mSearchPattern = new KMSearchPattern();
00169   KMFolderSearch *searchFolder = 0;
00170   if (curFolder)
00171       searchFolder = dynamic_cast<KMFolderSearch*>(curFolder->storage());
00172   if (searchFolder) {
00173       KConfig config(curFolder->location());
00174       KMFolder *root = searchFolder->search()->root();
00175       config.setGroup("Search Folder");
00176       mSearchPattern->readConfig(&config);
00177       if (root) {
00178           mChkbxSpecificFolders->setChecked(true);
00179           mCbxFolders->setFolder(root);
00180           mChkSubFolders->setChecked(searchFolder->search()->recursive());
00181       } else {
00182           mChkbxAllFolders->setChecked(true);
00183       }
00184       mFolder = searchFolder;
00185   }
00186   mPatternEdit->setSearchPattern( mSearchPattern );
00187   QObjectList *list = mPatternEdit->queryList( 0, "mRuleField" );
00188   QObject *object = 0;
00189   if ( list )
00190       object = list->first();
00191   delete list;
00192   if (!searchFolder && object && ::qt_cast<QComboBox*>(object))
00193       static_cast<QComboBox*>(object)->setCurrentText("Subject");
00194 
00195   vbl->addWidget( mPatternEdit );
00196 
00197   // enable/disable widgets depending on radio buttons:
00198   connect( mChkbxSpecificFolders, SIGNAL(toggled(bool)),
00199            mCbxFolders, SLOT(setEnabled(bool)) );
00200   connect( mChkbxSpecificFolders, SIGNAL(toggled(bool)),
00201            mChkSubFolders, SLOT(setEnabled(bool)) );
00202   connect( mChkbxAllFolders, SIGNAL(toggled(bool)),
00203            this, SLOT(setEnabledSearchButton(bool)) );
00204 
00205   mLbxMatches = new MatchListView(searchWidget, this, "Find Messages");
00206 
00207   /*
00208      Default is to sort by date. TODO: Unfortunately this sorts *while*
00209      inserting, which looks rather strange - the user cannot read
00210      the results so far as they are constantly re-sorted --dnaber
00211 
00212      Sorting is now disabled when a search is started and reenabled
00213      when it stops. Items are appended to the list. This not only
00214      solves the above problem, but speeds searches with many hits
00215      up considerably. - till
00216 
00217      TODO: subclass KListViewItem and do proper (and performant)
00218      comapare functions
00219   */
00220   mLbxMatches->setSorting(2, false);
00221   mLbxMatches->setShowSortIndicator(true);
00222   mLbxMatches->setAllColumnsShowFocus(true);
00223   mLbxMatches->setSelectionModeExt(KListView::Extended);
00224   mLbxMatches->addColumn(i18n("Subject"),
00225                          config->readNumEntry("SubjectWidth", 150));
00226   mLbxMatches->addColumn(i18n("Sender/Receiver"),
00227                          config->readNumEntry("SenderWidth", 120));
00228   mLbxMatches->addColumn(i18n("Date"),
00229                          config->readNumEntry("DateWidth", 120));
00230   mLbxMatches->addColumn(i18n("Folder"),
00231                          config->readNumEntry("FolderWidth", 100));
00232 
00233   mLbxMatches->addColumn(""); // should be hidden
00234   mLbxMatches->setColumnWidthMode( MSGID_COLUMN, QListView::Manual );
00235   mLbxMatches->setColumnWidth(MSGID_COLUMN, 0);
00236   mLbxMatches->header()->setResizeEnabled(false, MSGID_COLUMN);
00237 
00238   mLbxMatches->setDragEnabled( true );
00239 
00240   connect(mLbxMatches, SIGNAL(doubleClicked(QListViewItem *)),
00241           this, SLOT(slotShowMsg(QListViewItem *)));
00242   connect(mLbxMatches, SIGNAL(currentChanged(QListViewItem *)),
00243           this, SLOT(slotCurrentChanged(QListViewItem *)));
00244   connect( mLbxMatches, SIGNAL( contextMenuRequested( QListViewItem*, const QPoint &, int )),
00245            this, SLOT( slotContextMenuRequested( QListViewItem*, const QPoint &, int )));
00246   vbl->addWidget(mLbxMatches);
00247 
00248   QHBoxLayout *hbl2 = new QHBoxLayout( vbl, spacingHint(), "kmfs_hbl2" );
00249   mSearchFolderLbl = new QLabel(i18n("Search folder &name:"), searchWidget);
00250   hbl2->addWidget(mSearchFolderLbl);
00251   mSearchFolderEdt = new KLineEdit(searchWidget);
00252   if (searchFolder)
00253     mSearchFolderEdt->setText(searchFolder->folder()->name());
00254   else
00255     mSearchFolderEdt->setText(i18n("Last Search"));
00256 
00257   mSearchFolderLbl->setBuddy(mSearchFolderEdt);
00258   hbl2->addWidget(mSearchFolderEdt);
00259   mSearchFolderOpenBtn = new QPushButton(i18n("Op&en Search Folder"), searchWidget);
00260   mSearchFolderOpenBtn->setEnabled(false);
00261   hbl2->addWidget(mSearchFolderOpenBtn);
00262   connect( mSearchFolderEdt, SIGNAL( textChanged( const QString &)),
00263            this, SLOT( scheduleRename( const QString & )));
00264   connect( &mRenameTimer, SIGNAL( timeout() ),
00265            this, SLOT( renameSearchFolder() ));
00266   connect( mSearchFolderOpenBtn, SIGNAL( clicked() ),
00267            this, SLOT( openSearchFolder() ));
00268   mSearchResultOpenBtn = new QPushButton(i18n("Open &Message"), searchWidget);
00269   mSearchResultOpenBtn->setEnabled(false);
00270   hbl2->addWidget(mSearchResultOpenBtn);
00271   connect( mSearchResultOpenBtn, SIGNAL( clicked() ),
00272            this, SLOT( slotShowSelectedMsg() ));
00273   mStatusBar = new KStatusBar(searchWidget);
00274   mStatusBar->insertFixedItem(i18n("AMiddleLengthText..."), 0, true);
00275   mStatusBar->changeItem(i18n("Ready."), 0);
00276   mStatusBar->setItemAlignment(0, AlignLeft | AlignVCenter);
00277   mStatusBar->insertItem(QString::null, 1, 1, true);
00278   mStatusBar->setItemAlignment(1, AlignLeft | AlignVCenter);
00279   vbl->addWidget(mStatusBar);
00280 
00281   int mainWidth = config->readNumEntry("SearchWidgetWidth", 0);
00282   int mainHeight = config->readNumEntry("SearchWidgetHeight", 0);
00283 
00284   if (mainWidth || mainHeight)
00285     resize(mainWidth, mainHeight);
00286 
00287   setMainWidget(searchWidget);
00288   setButtonBoxOrientation(QWidget::Vertical);
00289 
00290   mBtnSearch = actionButton(KDialogBase::User1);
00291   mBtnStop = actionButton(KDialogBase::User2);
00292   mBtnStop->setEnabled(false);
00293 
00294   connect(this, SIGNAL(user1Clicked()), SLOT(slotSearch()));
00295   connect(this, SIGNAL(user2Clicked()), SLOT(slotStop()));
00296   connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));
00297 
00298   // give focus to the value field of the first search rule
00299   object = mPatternEdit->child( "regExpLineEdit" );
00300   if ( object && object->isWidgetType() ) {
00301       static_cast<QWidget*>(object)->setFocus();
00302       //kdDebug(5006) << "SearchWindow: focus has been given to widget "
00303       //              << object->name() << endl;
00304   }
00305   else
00306       kdDebug(5006) << "SearchWindow: regExpLineEdit not found" << endl;
00307 
00308   //set up actions
00309   KActionCollection *ac = actionCollection();
00310   ac->setWidget( this );
00311   mReplyAction = new KAction( i18n("&Reply..."), "mail_reply", 0, this,
00312                               SLOT(slotReplyToMsg()), ac, "search_reply" );
00313   mReplyAllAction = new KAction( i18n("Reply to &All..."), "mail_replyall",
00314                                  0, this, SLOT(slotReplyAllToMsg()),
00315                                  ac, "search_reply_all" );
00316   mReplyListAction = new KAction( i18n("Reply to Mailing-&List..."),
00317                                   "mail_replylist", 0, this,
00318                                   SLOT(slotReplyListToMsg()), ac,
00319                                   "search_reply_list" );
00320   mForwardActionMenu = new KActionMenu( i18n("Message->","&Forward"),
00321                                         "mail_forward", ac,
00322                                         "search_message_forward" );
00323   connect( mForwardActionMenu, SIGNAL(activated()), this,
00324            SLOT(slotForwardInlineMsg()) );
00325   mForwardAttachedAction = new KAction( i18n("Message->Forward->","As &Attachment..."),
00326                                         "mail_forward", 0, this,
00327                                         SLOT(slotForwardAttachedMsg()), ac,
00328                                         "search_message_forward_as_attachment" );
00329   mForwardInlineAction = new KAction( i18n("&Inline..."),
00330                                       "mail_forward", 0, this,
00331                                       SLOT(slotForwardInlineMsg()), ac,
00332                                       "search_message_forward_inline" );
00333   if ( GlobalSettings::self()->forwardingInlineByDefault() ) {
00334     mForwardActionMenu->insert( mForwardInlineAction );
00335     mForwardActionMenu->insert( mForwardAttachedAction );
00336   } else {
00337     mForwardActionMenu->insert( mForwardAttachedAction );
00338     mForwardActionMenu->insert( mForwardInlineAction );
00339   }
00340 
00341   mForwardDigestAction = new KAction( i18n("Message->Forward->","As Di&gest..."),
00342                                       "mail_forward", 0, this,
00343                                       SLOT(slotForwardDigestMsg()), ac,
00344                                       "search_message_forward_as_digest" );
00345   mForwardActionMenu->insert( mForwardDigestAction );
00346   mRedirectAction = new KAction( i18n("Message->Forward->","&Redirect..."),
00347                                       "mail_forward", 0, this,
00348                                       SLOT(slotRedirectMsg()), ac,
00349                                       "search_message_forward_redirect" );
00350   mForwardActionMenu->insert( mRedirectAction );
00351   mSaveAsAction = KStdAction::saveAs( this, SLOT(slotSaveMsg()), ac, "search_file_save_as" );
00352   mSaveAtchAction = new KAction( i18n("Save Attachments..."), "attach", 0,
00353                                  this, SLOT(slotSaveAttachments()), ac, "search_save_attachments" );
00354 
00355   mPrintAction = KStdAction::print( this, SLOT(slotPrintMsg()), ac, "search_print" );
00356   mClearAction = new KAction( i18n("Clear Selection"), 0, 0, this,
00357                               SLOT(slotClearSelection()), ac, "search_clear_selection" );
00358 
00359   mCopyAction = KStdAction::copy( this, SLOT(slotCopyMsgs()), ac, "search_copy_messages" );
00360   mCutAction = KStdAction::cut( this, SLOT(slotCutMsgs()), ac, "search_cut_messages" );
00361 
00362   connect(mTimer, SIGNAL(timeout()), this, SLOT(updStatus()));
00363   connect(kmkernel->searchFolderMgr(), SIGNAL(folderInvalidated(KMFolder*)),
00364           this, SLOT(folderInvalidated(KMFolder*)));
00365 
00366   connect(mCbxFolders, SIGNAL(folderChanged(KMFolder*)),
00367           this, SLOT(slotFolderActivated()));
00368 
00369 }
00370 
00371 //-----------------------------------------------------------------------------
00372 SearchWindow::~SearchWindow()
00373 {
00374   QValueListIterator<QGuardedPtr<KMFolder> > fit;
00375   for ( fit = mFolders.begin(); fit != mFolders.end(); ++fit ) {
00376     if (!(*fit))
00377       continue;
00378     (*fit)->close("searchwindow");
00379   }
00380 
00381   KConfig* config = KMKernel::config();
00382   config->setGroup("SearchDialog");
00383   config->writeEntry("SubjectWidth", mLbxMatches->columnWidth(0));
00384   config->writeEntry("SenderWidth", mLbxMatches->columnWidth(1));
00385   config->writeEntry("DateWidth", mLbxMatches->columnWidth(2));
00386   config->writeEntry("FolderWidth", mLbxMatches->columnWidth(3));
00387   config->writeEntry("SearchWidgetWidth", width());
00388   config->writeEntry("SearchWidgetHeight", height());
00389   config->sync();
00390 }
00391 
00392 void SearchWindow::setEnabledSearchButton(bool)
00393 {
00394   //Make sure that button is enable
00395   //Before when we selected a folder == "Local Folder" as that it was not a folder
00396   //search button was disable, and when we select "Search in all local folder"
00397   //Search button was never enabled :(
00398   mBtnSearch->setEnabled( true );
00399 }
00400 
00401 //-----------------------------------------------------------------------------
00402 void SearchWindow::updStatus(void)
00403 {
00404     QString genMsg, detailMsg, procMsg;
00405     int numMatches = 0, numProcessed = 0;
00406     KMSearch const *search = (mFolder) ? (mFolder->search()) : 0;
00407     QString folderName;
00408     if (search) {
00409         numMatches = search->foundCount();
00410         numProcessed = search->searchCount();
00411         folderName = search->currentFolder();
00412     }
00413 
00414     if (search && !search->running()) {
00415         procMsg = i18n("%n message searched", "%n messages searched",
00416                        numProcessed);
00417         if(!mStopped) {
00418             genMsg = i18n("Done.");
00419             detailMsg = i18n("%n match in %1", "%n matches in %1",
00420                              numMatches).arg(procMsg);
00421         } else {
00422             genMsg = i18n("Search canceled.");
00423             detailMsg = i18n("%n match so far in %1", "%n matches so far in %1",
00424                              numMatches).arg(procMsg);
00425         }
00426     } else {
00427         procMsg = i18n("%n message", "%n messages", numProcessed);
00428         genMsg = i18n("%n match", "%n matches", numMatches);
00429         detailMsg = i18n("Searching in %1. %2 searched so far")
00430                     .arg(folderName).arg(procMsg);
00431     }
00432 
00433     mStatusBar->changeItem(genMsg, 0);
00434     mStatusBar->changeItem(detailMsg, 1);
00435 }
00436 
00437 
00438 //-----------------------------------------------------------------------------
00439 void SearchWindow::keyPressEvent(QKeyEvent *evt)
00440 {
00441     KMSearch const *search = (mFolder) ? mFolder->search() : 0;
00442     bool searching = (search) ? search->running() : false;
00443     if (evt->key() == Key_Escape && searching) {
00444         mFolder->stopSearch();
00445         return;
00446     }
00447 
00448     KDialogBase::keyPressEvent(evt);
00449 }
00450 
00451 
00452 //-----------------------------------------------------------------------------
00453 void SearchWindow::slotFolderActivated()
00454 {
00455     mChkbxSpecificFolders->setChecked(true);
00456 }
00457 
00458 //-----------------------------------------------------------------------------
00459 void SearchWindow::activateFolder(KMFolder *curFolder)
00460 {
00461     mChkbxSpecificFolders->setChecked(true);
00462     mCbxFolders->setFolder(curFolder);
00463 }
00464 
00465 //-----------------------------------------------------------------------------
00466 void SearchWindow::slotSearch()
00467 {
00468     mLastFocus = focusWidget();
00469     mBtnSearch->setFocus();     // set focus so we don't miss key event
00470 
00471     mStopped = false;
00472     mFetchingInProgress = 0;
00473 
00474     mSearchFolderOpenBtn->setEnabled(true);
00475     if ( mSearchFolderEdt->text().isEmpty() ) {                     
00476       mSearchFolderEdt->setText( i18n("Last Search") );             
00477     }  
00478     mBtnSearch->setEnabled(false);
00479     mBtnStop->setEnabled(true);
00480 
00481     mLbxMatches->clear();
00482 
00483     mSortColumn = mLbxMatches->sortColumn();
00484     mSortOrder = mLbxMatches->sortOrder();
00485     mLbxMatches->setSorting(-1);
00486     mLbxMatches->setShowSortIndicator(false);
00487 
00488     // If we haven't openend an existing search folder, find or
00489     // create one.
00490     if (!mFolder) {
00491       KMFolderMgr *mgr = kmkernel->searchFolderMgr();
00492       QString baseName = mSearchFolderEdt->text();
00493       QString fullName = baseName;
00494       int count = 0;
00495       KMFolder *folder;
00496       while ((folder = mgr->find(fullName))) {
00497         if (folder->storage()->inherits("KMFolderSearch"))
00498           break;
00499         fullName = QString("%1 %2").arg(baseName).arg(++count);
00500       }
00501 
00502       if (!folder)
00503         folder = mgr->createFolder(fullName, false, KMFolderTypeSearch,
00504             &mgr->dir());
00505 
00506       mFolder = dynamic_cast<KMFolderSearch*>( folder->storage() );
00507     }
00508     mFolder->stopSearch();
00509     disconnect(mFolder, SIGNAL(msgAdded(int)),
00510             this, SLOT(slotAddMsg(int)));
00511     disconnect(mFolder, SIGNAL(msgRemoved(KMFolder*, Q_UINT32)),
00512             this, SLOT(slotRemoveMsg(KMFolder*, Q_UINT32)));
00513     connect(mFolder, SIGNAL(msgAdded(int)),
00514             this, SLOT(slotAddMsg(int)));
00515     connect(mFolder, SIGNAL(msgRemoved(KMFolder*, Q_UINT32)),
00516             this, SLOT(slotRemoveMsg(KMFolder*, Q_UINT32)));
00517     mSearchFolderEdt->setEnabled(false);
00518     KMSearch *search = new KMSearch();
00519     connect(search, SIGNAL(finished(bool)),
00520             this, SLOT(searchDone()));
00521     if (mChkbxAllFolders->isChecked()) {
00522         search->setRecursive(true);
00523     } else {
00524         search->setRoot(mCbxFolders->folder());
00525         search->setRecursive(mChkSubFolders->isChecked());
00526     }
00527 
00528     mPatternEdit->updateSearchPattern();
00529     KMSearchPattern *searchPattern = new KMSearchPattern();
00530     *searchPattern = *mSearchPattern; //deep copy
00531     searchPattern->purify();
00532     search->setSearchPattern(searchPattern);
00533     mFolder->setSearch(search);
00534     enableGUI();
00535 
00536     mTimer->start(200);
00537 }
00538 
00539 //-----------------------------------------------------------------------------
00540 void SearchWindow::searchDone()
00541 {
00542     mTimer->stop();
00543     updStatus();
00544 
00545     QTimer::singleShot(0, this, SLOT(enableGUI()));
00546     if(mLastFocus)
00547         mLastFocus->setFocus();
00548     if (mCloseRequested)
00549         close();
00550 
00551     mLbxMatches->setSorting(mSortColumn, mSortOrder == Ascending);
00552     mLbxMatches->setShowSortIndicator(true);
00553 
00554     mSearchFolderEdt->setEnabled(true);
00555 }
00556 
00557 void SearchWindow::slotAddMsg(int idx)
00558 {
00559     if (!mFolder)
00560         return;
00561     bool unget = !mFolder->isMessage(idx);
00562     KMMessage *msg = mFolder->getMsg(idx);
00563     QString from, fName;
00564     KMFolder *pFolder = msg->parent();
00565     if (!mFolders.contains(pFolder)) {
00566         mFolders.append(pFolder);
00567         pFolder->open("searchwindow");
00568     }
00569     if(pFolder->whoField() == "To")
00570         from = msg->to();
00571     else
00572         from = msg->from();
00573     if (pFolder->isSystemFolder())
00574         fName = i18n(pFolder->name().utf8());
00575     else
00576         fName = pFolder->name();
00577 
00578     (void)new KListViewItem(mLbxMatches, mLbxMatches->lastItem(),
00579                             msg->subject(), from, msg->dateIsoStr(),
00580                             fName,
00581                             QString::number(mFolder->serNum(idx)));
00582     if (unget)
00583         mFolder->unGetMsg(idx);
00584 }
00585 
00586 void SearchWindow::slotRemoveMsg(KMFolder *, Q_UINT32 serNum)
00587 {
00588     if (!mFolder)
00589         return;
00590     QListViewItemIterator it(mLbxMatches);
00591     while (it.current()) {
00592         QListViewItem *item = *it;
00593         if (serNum == (*it)->text(MSGID_COLUMN).toUInt()) {
00594             delete item;
00595             return;
00596         }
00597         ++it;
00598     }
00599 }
00600 
00601 //-----------------------------------------------------------------------------
00602 void SearchWindow::slotStop()
00603 {
00604     if (mFolder)
00605       mFolder->stopSearch();
00606     mStopped = true;
00607     mBtnStop->setEnabled(false);
00608 }
00609 
00610 //-----------------------------------------------------------------------------
00611 void SearchWindow::slotClose()
00612 {
00613     accept();
00614 }
00615 
00616 
00617 //-----------------------------------------------------------------------------
00618 void SearchWindow::closeEvent(QCloseEvent *e)
00619 {
00620     if (mFolder && mFolder->search() && mFolder->search()->running()) {
00621       mCloseRequested = true;
00622       //Cancel search in progress by setting the search folder search to
00623       //the null search
00624       mFolder->setSearch(new KMSearch());
00625       QTimer::singleShot(0, this, SLOT(slotClose()));
00626     } else {
00627       KDialogBase::closeEvent(e);
00628     }
00629 }
00630 
00631 //-----------------------------------------------------------------------------
00632 void SearchWindow::scheduleRename( const QString &s)
00633 {
00634     if (!s.isEmpty() ) {
00635       mRenameTimer.start(250, true);
00636       mSearchFolderOpenBtn->setEnabled(false);
00637     } else {
00638       mRenameTimer.stop();
00639       mSearchFolderOpenBtn->setEnabled(!s.isEmpty());
00640     }
00641 }
00642 
00643 //-----------------------------------------------------------------------------
00644 void SearchWindow::renameSearchFolder()
00645 {
00646     if (mFolder && (mFolder->folder()->name() != mSearchFolderEdt->text())) {
00647         int i = 1;
00648         QString name =  mSearchFolderEdt->text();
00649         while (i < 100) {
00650             if (!kmkernel->searchFolderMgr()->find( name )) {
00651                 mFolder->rename( name );
00652                 kmkernel->searchFolderMgr()->contentsChanged();
00653                 break;
00654             }
00655             name.setNum( i );
00656             name = mSearchFolderEdt->text() + " " + name;
00657             ++i;
00658         }
00659     }
00660     if ( mFolder )
00661       mSearchFolderOpenBtn->setEnabled(true);
00662 }
00663 
00664 void SearchWindow::openSearchFolder()
00665 {
00666   Q_ASSERT( mFolder );
00667     renameSearchFolder();
00668     mKMMainWidget->slotSelectFolder( mFolder->folder() );
00669     slotClose();
00670 }
00671 
00672 //-----------------------------------------------------------------------------
00673 void SearchWindow::folderInvalidated(KMFolder *folder)
00674 {
00675     if (folder->storage() == mFolder) {
00676         mLbxMatches->clear();
00677         if (mFolder->search())
00678             connect(mFolder->search(), SIGNAL(finished(bool)),
00679                     this, SLOT(searchDone()));
00680         mTimer->start(200);
00681         enableGUI();
00682     }
00683 }
00684 
00685 //-----------------------------------------------------------------------------
00686 bool SearchWindow::slotShowMsg(QListViewItem *item)
00687 {
00688     if(!item)
00689         return false;
00690 
00691     KMFolder* folder;
00692     int msgIndex;
00693     KMMsgDict::instance()->getLocation(item->text(MSGID_COLUMN).toUInt(),
00694                                    &folder, &msgIndex);
00695 
00696     if (!folder || msgIndex < 0)
00697         return false;
00698 
00699     mKMMainWidget->slotSelectFolder(folder);
00700     KMMessage* message = folder->getMsg(msgIndex);
00701     if (!message)
00702         return false;
00703 
00704     mKMMainWidget->slotSelectMessage(message);
00705     return true;
00706 }
00707 
00708 //-----------------------------------------------------------------------------
00709 void SearchWindow::slotShowSelectedMsg()
00710 {
00711     slotShowMsg(mLbxMatches->currentItem());
00712 }
00713 
00714 //-----------------------------------------------------------------------------
00715 void SearchWindow::slotCurrentChanged(QListViewItem *item)
00716 {
00717   mSearchResultOpenBtn->setEnabled(item!=0);
00718 }
00719 
00720 //-----------------------------------------------------------------------------
00721 void SearchWindow::enableGUI()
00722 {
00723     KMSearch const *search = (mFolder) ? (mFolder->search()) : 0;
00724     bool searching = (search) ? (search->running()) : false;
00725     actionButton(KDialogBase::Close)->setEnabled(!searching);
00726     mCbxFolders->setEnabled(!searching && !mChkbxAllFolders->isChecked());
00727     mChkSubFolders->setEnabled(!searching && !mChkbxAllFolders->isChecked());
00728     mChkbxAllFolders->setEnabled(!searching);
00729     mChkbxSpecificFolders->setEnabled(!searching);
00730     mPatternEdit->setEnabled(!searching);
00731     mBtnSearch->setEnabled(!searching);
00732     mBtnStop->setEnabled(searching);
00733 }
00734 
00735 
00736 //-----------------------------------------------------------------------------
00737 KMMessageList SearchWindow::selectedMessages()
00738 {
00739     KMMessageList msgList;
00740     KMFolder* folder = 0;
00741     int msgIndex = -1;
00742     for (QListViewItemIterator it(mLbxMatches); it.current(); it++)
00743         if (it.current()->isSelected()) {
00744             KMMsgDict::instance()->getLocation((*it)->text(MSGID_COLUMN).toUInt(),
00745                                            &folder, &msgIndex);
00746             if (folder && msgIndex >= 0)
00747                 msgList.append(folder->getMsgBase(msgIndex));
00748         }
00749     return msgList;
00750 }
00751 
00752 //-----------------------------------------------------------------------------
00753 KMMessage* SearchWindow::message()
00754 {
00755     QListViewItem *item = mLbxMatches->currentItem();
00756     KMFolder* folder = 0;
00757     int msgIndex = -1;
00758     if (!item)
00759         return 0;
00760     KMMsgDict::instance()->getLocation(item->text(MSGID_COLUMN).toUInt(),
00761                                    &folder, &msgIndex);
00762     if (!folder || msgIndex < 0)
00763         return 0;
00764 
00765     return folder->getMsg(msgIndex);
00766 }
00767 
00768 //-----------------------------------------------------------------------------
00769 void SearchWindow::moveSelectedToFolder( int menuId )
00770 {
00771     KMFolder *dest = mMenuToFolder[menuId];
00772     if (!dest)
00773         return;
00774 
00775     KMMessageList msgList = selectedMessages();
00776     KMCommand *command = new KMMoveCommand( dest, msgList );
00777     command->start();
00778 }
00779 
00780 //-----------------------------------------------------------------------------
00781 void SearchWindow::copySelectedToFolder( int menuId )
00782 {
00783     KMFolder *dest = mMenuToFolder[menuId];
00784     if (!dest)
00785         return;
00786 
00787     KMMessageList msgList = selectedMessages();
00788     KMCommand *command = new KMCopyCommand( dest, msgList );
00789     command->start();
00790 }
00791 
00792 //-----------------------------------------------------------------------------
00793 void SearchWindow::updateContextMenuActions()
00794 {
00795     int count = selectedMessages().count();
00796     bool single_actions = count == 1;
00797     mReplyAction->setEnabled( single_actions );
00798     mReplyAllAction->setEnabled( single_actions );
00799     mReplyListAction->setEnabled( single_actions );
00800     mPrintAction->setEnabled( single_actions );
00801     mForwardDigestAction->setEnabled( !single_actions );
00802     mRedirectAction->setEnabled( single_actions );
00803     mCopyAction->setEnabled( count > 0 );
00804     mCutAction->setEnabled( count > 0 );
00805 }
00806 
00807 //-----------------------------------------------------------------------------
00808 void SearchWindow::slotContextMenuRequested( QListViewItem *lvi, const QPoint &, int )
00809 {
00810     if (!lvi)
00811         return;
00812     mLbxMatches->setSelected( lvi, true );
00813     mLbxMatches->setCurrentItem( lvi );
00814     // FIXME is this ever unGetMsg()'d?
00815     if (!message())
00816         return;
00817     QPopupMenu *menu = new QPopupMenu(this);
00818     updateContextMenuActions();
00819 
00820     mMenuToFolder.clear();
00821     QPopupMenu *msgMoveMenu = new QPopupMenu(menu);
00822     mKMMainWidget->folderTree()->folderToPopupMenu( KMFolderTree::MoveMessage,
00823         this, &mMenuToFolder, msgMoveMenu );
00824     QPopupMenu *msgCopyMenu = new QPopupMenu(menu);
00825     mKMMainWidget->folderTree()->folderToPopupMenu( KMFolderTree::CopyMessage,
00826         this, &mMenuToFolder, msgCopyMenu );
00827 
00828     // show most used actions
00829     mReplyAction->plug(menu);
00830     mReplyAllAction->plug(menu);
00831     mReplyListAction->plug(menu);
00832     mForwardActionMenu->plug(menu);
00833     menu->insertSeparator();
00834     mCopyAction->plug(menu);
00835     mCutAction->plug(menu);
00836     menu->insertItem(i18n("&Copy To"), msgCopyMenu);
00837     menu->insertItem(i18n("&Move To"), msgMoveMenu);
00838     menu->insertSeparator();
00839     mSaveAsAction->plug(menu);
00840     mSaveAtchAction->plug(menu);
00841     mPrintAction->plug(menu);
00842     menu->insertSeparator();
00843     mClearAction->plug(menu);
00844     menu->exec (QCursor::pos(), 0);
00845     delete menu;
00846 }
00847 
00848 //-----------------------------------------------------------------------------
00849 void SearchWindow::slotClearSelection()
00850 {
00851     mLbxMatches->clearSelection();
00852 }
00853 
00854 //-----------------------------------------------------------------------------
00855 void SearchWindow::slotReplyToMsg()
00856 {
00857     KMCommand *command = new KMReplyToCommand(this, message());
00858     command->start();
00859 }
00860 
00861 //-----------------------------------------------------------------------------
00862 void SearchWindow::slotReplyAllToMsg()
00863 {
00864     KMCommand *command = new KMReplyToAllCommand(this, message());
00865     command->start();
00866 }
00867 
00868 //-----------------------------------------------------------------------------
00869 void SearchWindow::slotReplyListToMsg()
00870 {
00871     KMCommand *command = new KMReplyListCommand(this, message());
00872     command->start();
00873 }
00874 
00875 //-----------------------------------------------------------------------------
00876 void SearchWindow::slotForwardInlineMsg()
00877 {
00878     KMCommand *command = new KMForwardInlineCommand(this, selectedMessages());
00879     command->start();
00880 }
00881 
00882 //-----------------------------------------------------------------------------
00883 void SearchWindow::slotForwardAttachedMsg()
00884 {
00885     KMCommand *command = new KMForwardAttachedCommand(this, selectedMessages());
00886     command->start();
00887 }
00888 
00889 //-----------------------------------------------------------------------------
00890 void SearchWindow::slotForwardDigestMsg()
00891 {
00892     KMCommand *command = new KMForwardDigestCommand(this, selectedMessages());
00893     command->start();
00894 }
00895 
00896 //-----------------------------------------------------------------------------
00897 void SearchWindow::slotRedirectMsg()
00898 {
00899     KMCommand *command = new KMRedirectCommand(this, message());
00900     command->start();
00901 }
00902 
00903 //-----------------------------------------------------------------------------
00904 void SearchWindow::slotSaveMsg()
00905 {
00906     KMSaveMsgCommand *saveCommand = new KMSaveMsgCommand(this,
00907                                                          selectedMessages());
00908     if (saveCommand->url().isEmpty())
00909         delete saveCommand;
00910     else
00911         saveCommand->start();
00912 }
00913 //-----------------------------------------------------------------------------
00914 void SearchWindow::slotSaveAttachments()
00915 {
00916     KMSaveAttachmentsCommand *saveCommand = new KMSaveAttachmentsCommand(this,
00917                                                                          selectedMessages());
00918     saveCommand->start();
00919 }
00920 
00921 
00922 //-----------------------------------------------------------------------------
00923 void SearchWindow::slotPrintMsg()
00924 {
00925     KMCommand *command = new KMPrintCommand(this, message());
00926     command->start();
00927 }
00928 
00929 void SearchWindow::slotCopyMsgs()
00930 {
00931   QValueList<Q_UINT32> list = MessageCopyHelper::serNumListFromMsgList( selectedMessages() );
00932   mKMMainWidget->headers()->setCopiedMessages( list, false );
00933 }
00934 
00935 void SearchWindow::slotCutMsgs()
00936 {
00937   QValueList<Q_UINT32> list = MessageCopyHelper::serNumListFromMsgList( selectedMessages() );
00938   mKMMainWidget->headers()->setCopiedMessages( list, true );
00939 }
00940 
00941 
00942 void SearchWindow::setSearchPattern( const KMSearchPattern& pattern )
00943 {
00944     *mSearchPattern = pattern;
00945     mPatternEdit->setSearchPattern( mSearchPattern );
00946 }
00947 
00948 } // namespace KMail
00949 #include "searchwindow.moc"