kmail

searchwindow.cpp

Go to the documentation of this file.
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     mBtnSearch->setEnabled(false);
00476     mBtnStop->setEnabled(true);
00477 
00478     mLbxMatches->clear();
00479 
00480     mSortColumn = mLbxMatches->sortColumn();
00481     mSortOrder = mLbxMatches->sortOrder();
00482     mLbxMatches->setSorting(-1);
00483     mLbxMatches->setShowSortIndicator(false);
00484 
00485     // If we haven't openend an existing search folder, find or
00486     // create one.
00487     if (!mFolder) {
00488       KMFolderMgr *mgr = kmkernel->searchFolderMgr();
00489       if (mSearchFolderEdt->text().isEmpty())
00490           mSearchFolderEdt->setText(i18n("Last Search"));
00491       QString baseName = mSearchFolderEdt->text();
00492       QString fullName = baseName;
00493       int count = 0;
00494       KMFolder *folder;
00495       while ((folder = mgr->find(fullName))) {
00496         if (folder->storage()->inherits("KMFolderSearch"))
00497           break;
00498         fullName = QString("%1 %2").arg(baseName).arg(++count);
00499       }
00500 
00501       if (!folder)
00502         folder = mgr->createFolder(fullName, false, KMFolderTypeSearch,
00503             &mgr->dir());
00504 
00505       mFolder = dynamic_cast<KMFolderSearch*>( folder->storage() );
00506     }
00507     mFolder->stopSearch();
00508     disconnect(mFolder, SIGNAL(msgAdded(int)),
00509             this, SLOT(slotAddMsg(int)));
00510     disconnect(mFolder, SIGNAL(msgRemoved(KMFolder*, Q_UINT32)),
00511             this, SLOT(slotRemoveMsg(KMFolder*, Q_UINT32)));
00512     connect(mFolder, SIGNAL(msgAdded(int)),
00513             this, SLOT(slotAddMsg(int)));
00514     connect(mFolder, SIGNAL(msgRemoved(KMFolder*, Q_UINT32)),
00515             this, SLOT(slotRemoveMsg(KMFolder*, Q_UINT32)));
00516     mSearchFolderEdt->setEnabled(false);
00517     KMSearch *search = new KMSearch();
00518     connect(search, SIGNAL(finished(bool)),
00519             this, SLOT(searchDone()));
00520     if (mChkbxAllFolders->isChecked()) {
00521         search->setRecursive(true);
00522     } else {
00523         search->setRoot(mCbxFolders->folder());
00524         search->setRecursive(mChkSubFolders->isChecked());
00525     }
00526 
00527     mPatternEdit->updateSearchPattern();
00528     KMSearchPattern *searchPattern = new KMSearchPattern();
00529     *searchPattern = *mSearchPattern; //deep copy
00530     searchPattern->purify();
00531     search->setSearchPattern(searchPattern);
00532     mFolder->setSearch(search);
00533     enableGUI();
00534 
00535     mTimer->start(200);
00536 }
00537 
00538 //-----------------------------------------------------------------------------
00539 void SearchWindow::searchDone()
00540 {
00541     mTimer->stop();
00542     updStatus();
00543 
00544     QTimer::singleShot(0, this, SLOT(enableGUI()));
00545     if(mLastFocus)
00546         mLastFocus->setFocus();
00547     if (mCloseRequested)
00548         close();
00549 
00550     mLbxMatches->setSorting(mSortColumn, mSortOrder == Ascending);
00551     mLbxMatches->setShowSortIndicator(true);
00552 
00553     mSearchFolderEdt->setEnabled(true);
00554 }
00555 
00556 void SearchWindow::slotAddMsg(int idx)
00557 {
00558     if (!mFolder)
00559         return;
00560     bool unget = !mFolder->isMessage(idx);
00561     KMMessage *msg = mFolder->getMsg(idx);
00562     QString from, fName;
00563     KMFolder *pFolder = msg->parent();
00564     if (!mFolders.contains(pFolder)) {
00565         mFolders.append(pFolder);
00566         pFolder->open("searchwindow");
00567     }
00568     if(pFolder->whoField() == "To")
00569         from = msg->to();
00570     else
00571         from = msg->from();
00572     if (pFolder->isSystemFolder())
00573         fName = i18n(pFolder->name().utf8());
00574     else
00575         fName = pFolder->name();
00576 
00577     (void)new KListViewItem(mLbxMatches, mLbxMatches->lastItem(),
00578                             msg->subject(), from, msg->dateIsoStr(),
00579                             fName,
00580                             QString::number(mFolder->serNum(idx)));
00581     if (unget)
00582         mFolder->unGetMsg(idx);
00583 }
00584 
00585 void SearchWindow::slotRemoveMsg(KMFolder *, Q_UINT32 serNum)
00586 {
00587     if (!mFolder)
00588         return;
00589     QListViewItemIterator it(mLbxMatches);
00590     while (it.current()) {
00591         QListViewItem *item = *it;
00592         if (serNum == (*it)->text(MSGID_COLUMN).toUInt()) {
00593             delete item;
00594             return;
00595         }
00596         ++it;
00597     }
00598 }
00599 
00600 //-----------------------------------------------------------------------------
00601 void SearchWindow::slotStop()
00602 {
00603     if (mFolder)
00604       mFolder->stopSearch();
00605     mStopped = true;
00606     mBtnStop->setEnabled(false);
00607 }
00608 
00609 //-----------------------------------------------------------------------------
00610 void SearchWindow::slotClose()
00611 {
00612     accept();
00613 }
00614 
00615 
00616 //-----------------------------------------------------------------------------
00617 void SearchWindow::closeEvent(QCloseEvent *e)
00618 {
00619     if (mFolder && mFolder->search() && mFolder->search()->running()) {
00620       mCloseRequested = true;
00621       //Cancel search in progress by setting the search folder search to
00622       //the null search
00623       mFolder->setSearch(new KMSearch());
00624       QTimer::singleShot(0, this, SLOT(slotClose()));
00625     } else {
00626       KDialogBase::closeEvent(e);
00627     }
00628 }
00629 
00630 //-----------------------------------------------------------------------------
00631 void SearchWindow::scheduleRename( const QString &s)
00632 {
00633     if (!s.isEmpty() && s != i18n("Last Search")) {
00634       mRenameTimer.start(250, true);
00635       mSearchFolderOpenBtn->setEnabled(false);
00636     } else {
00637       mRenameTimer.stop();
00638       mSearchFolderOpenBtn->setEnabled(true);
00639     }
00640 }
00641 
00642 //-----------------------------------------------------------------------------
00643 void SearchWindow::renameSearchFolder()
00644 {
00645     if (mFolder && (mFolder->folder()->name() != mSearchFolderEdt->text())) {
00646         int i = 1;
00647         QString name =  mSearchFolderEdt->text();
00648         while (i < 100) {
00649             if (!kmkernel->searchFolderMgr()->find( name )) {
00650                 mFolder->rename( name );
00651                 kmkernel->searchFolderMgr()->contentsChanged();
00652                 break;
00653             }
00654             name.setNum( i );
00655             name = mSearchFolderEdt->text() + " " + name;
00656             ++i;
00657         }
00658     }
00659     mSearchFolderOpenBtn->setEnabled(true);
00660 }
00661 
00662 void SearchWindow::openSearchFolder()
00663 {
00664     renameSearchFolder();
00665     mKMMainWidget->slotSelectFolder( mFolder->folder() );
00666     slotClose();
00667 }
00668 
00669 //-----------------------------------------------------------------------------
00670 void SearchWindow::folderInvalidated(KMFolder *folder)
00671 {
00672     if (folder->storage() == mFolder) {
00673         mLbxMatches->clear();
00674         if (mFolder->search())
00675             connect(mFolder->search(), SIGNAL(finished(bool)),
00676                     this, SLOT(searchDone()));
00677         mTimer->start(200);
00678         enableGUI();
00679     }
00680 }
00681 
00682 //-----------------------------------------------------------------------------
00683 bool SearchWindow::slotShowMsg(QListViewItem *item)
00684 {
00685     if(!item)
00686         return false;
00687 
00688     KMFolder* folder;
00689     int msgIndex;
00690     KMMsgDict::instance()->getLocation(item->text(MSGID_COLUMN).toUInt(),
00691                                    &folder, &msgIndex);
00692 
00693     if (!folder || msgIndex < 0)
00694         return false;
00695 
00696     mKMMainWidget->slotSelectFolder(folder);
00697     KMMessage* message = folder->getMsg(msgIndex);
00698     if (!message)
00699         return false;
00700 
00701     mKMMainWidget->slotSelectMessage(message);
00702     return true;
00703 }
00704 
00705 //-----------------------------------------------------------------------------
00706 void SearchWindow::slotShowSelectedMsg()
00707 {
00708     slotShowMsg(mLbxMatches->currentItem());
00709 }
00710 
00711 //-----------------------------------------------------------------------------
00712 void SearchWindow::slotCurrentChanged(QListViewItem *item)
00713 {
00714   mSearchResultOpenBtn->setEnabled(item!=0);
00715 }
00716 
00717 //-----------------------------------------------------------------------------
00718 void SearchWindow::enableGUI()
00719 {
00720     KMSearch const *search = (mFolder) ? (mFolder->search()) : 0;
00721     bool searching = (search) ? (search->running()) : false;
00722     actionButton(KDialogBase::Close)->setEnabled(!searching);
00723     mCbxFolders->setEnabled(!searching);
00724     mChkSubFolders->setEnabled(!searching);
00725     mChkbxAllFolders->setEnabled(!searching);
00726     mChkbxSpecificFolders->setEnabled(!searching);
00727     mPatternEdit->setEnabled(!searching);
00728     mBtnSearch->setEnabled(!searching);
00729     mBtnStop->setEnabled(searching);
00730 }
00731 
00732 
00733 //-----------------------------------------------------------------------------
00734 KMMessageList SearchWindow::selectedMessages()
00735 {
00736     KMMessageList msgList;
00737     KMFolder* folder = 0;
00738     int msgIndex = -1;
00739     for (QListViewItemIterator it(mLbxMatches); it.current(); it++)
00740         if (it.current()->isSelected()) {
00741             KMMsgDict::instance()->getLocation((*it)->text(MSGID_COLUMN).toUInt(),
00742                                            &folder, &msgIndex);
00743             if (folder && msgIndex >= 0)
00744                 msgList.append(folder->getMsgBase(msgIndex));
00745         }
00746     return msgList;
00747 }
00748 
00749 //-----------------------------------------------------------------------------
00750 KMMessage* SearchWindow::message()
00751 {
00752     QListViewItem *item = mLbxMatches->currentItem();
00753     KMFolder* folder = 0;
00754     int msgIndex = -1;
00755     if (!item)
00756         return 0;
00757     KMMsgDict::instance()->getLocation(item->text(MSGID_COLUMN).toUInt(),
00758                                    &folder, &msgIndex);
00759     if (!folder || msgIndex < 0)
00760         return 0;
00761 
00762     return folder->getMsg(msgIndex);
00763 }
00764 
00765 //-----------------------------------------------------------------------------
00766 void SearchWindow::moveSelectedToFolder( int menuId )
00767 {
00768     KMFolder *dest = mMenuToFolder[menuId];
00769     if (!dest)
00770         return;
00771 
00772     KMMessageList msgList = selectedMessages();
00773     KMCommand *command = new KMMoveCommand( dest, msgList );
00774     command->start();
00775 }
00776 
00777 //-----------------------------------------------------------------------------
00778 void SearchWindow::copySelectedToFolder( int menuId )
00779 {
00780     KMFolder *dest = mMenuToFolder[menuId];
00781     if (!dest)
00782         return;
00783 
00784     KMMessageList msgList = selectedMessages();
00785     KMCommand *command = new KMCopyCommand( dest, msgList );
00786     command->start();
00787 }
00788 
00789 //-----------------------------------------------------------------------------
00790 void SearchWindow::updateContextMenuActions()
00791 {
00792     int count = selectedMessages().count();
00793     bool single_actions = count == 1;
00794     mReplyAction->setEnabled( single_actions );
00795     mReplyAllAction->setEnabled( single_actions );
00796     mReplyListAction->setEnabled( single_actions );
00797     mPrintAction->setEnabled( single_actions );
00798     mForwardDigestAction->setEnabled( !single_actions );
00799     mRedirectAction->setEnabled( single_actions );
00800     mCopyAction->setEnabled( count > 0 );
00801     mCutAction->setEnabled( count > 0 );
00802 }
00803 
00804 //-----------------------------------------------------------------------------
00805 void SearchWindow::slotContextMenuRequested( QListViewItem *lvi, const QPoint &, int )
00806 {
00807     if (!lvi)
00808         return;
00809     mLbxMatches->setSelected( lvi, true );
00810     mLbxMatches->setCurrentItem( lvi );
00811     // FIXME is this ever unGetMsg()'d?
00812     if (!message())
00813         return;
00814     QPopupMenu *menu = new QPopupMenu(this);
00815     updateContextMenuActions();
00816 
00817     mMenuToFolder.clear();
00818     QPopupMenu *msgMoveMenu = new QPopupMenu(menu);
00819     mKMMainWidget->folderTree()->folderToPopupMenu( KMFolderTree::MoveMessage,
00820         this, &mMenuToFolder, msgMoveMenu );
00821     QPopupMenu *msgCopyMenu = new QPopupMenu(menu);
00822     mKMMainWidget->folderTree()->folderToPopupMenu( KMFolderTree::CopyMessage,
00823         this, &mMenuToFolder, msgCopyMenu );
00824 
00825     // show most used actions
00826     mReplyAction->plug(menu);
00827     mReplyAllAction->plug(menu);
00828     mReplyListAction->plug(menu);
00829     mForwardActionMenu->plug(menu);
00830     menu->insertSeparator();
00831     mCopyAction->plug(menu);
00832     mCutAction->plug(menu);
00833     menu->insertItem(i18n("&Copy To"), msgCopyMenu);
00834     menu->insertItem(i18n("&Move To"), msgMoveMenu);
00835     menu->insertSeparator();
00836     mSaveAsAction->plug(menu);
00837     mSaveAtchAction->plug(menu);
00838     mPrintAction->plug(menu);
00839     menu->insertSeparator();
00840     mClearAction->plug(menu);
00841     menu->exec (QCursor::pos(), 0);
00842     delete menu;
00843 }
00844 
00845 //-----------------------------------------------------------------------------
00846 void SearchWindow::slotClearSelection()
00847 {
00848     mLbxMatches->clearSelection();
00849 }
00850 
00851 //-----------------------------------------------------------------------------
00852 void SearchWindow::slotReplyToMsg()
00853 {
00854     KMCommand *command = new KMReplyToCommand(this, message());
00855     command->start();
00856 }
00857 
00858 //-----------------------------------------------------------------------------
00859 void SearchWindow::slotReplyAllToMsg()
00860 {
00861     KMCommand *command = new KMReplyToAllCommand(this, message());
00862     command->start();
00863 }
00864 
00865 //-----------------------------------------------------------------------------
00866 void SearchWindow::slotReplyListToMsg()
00867 {
00868     KMCommand *command = new KMReplyListCommand(this, message());
00869     command->start();
00870 }
00871 
00872 //-----------------------------------------------------------------------------
00873 void SearchWindow::slotForwardInlineMsg()
00874 {
00875     KMCommand *command = new KMForwardInlineCommand(this, selectedMessages());
00876     command->start();
00877 }
00878 
00879 //-----------------------------------------------------------------------------
00880 void SearchWindow::slotForwardAttachedMsg()
00881 {
00882     KMCommand *command = new KMForwardAttachedCommand(this, selectedMessages());
00883     command->start();
00884 }
00885 
00886 //-----------------------------------------------------------------------------
00887 void SearchWindow::slotForwardDigestMsg()
00888 {
00889     KMCommand *command = new KMForwardDigestCommand(this, selectedMessages());
00890     command->start();
00891 }
00892 
00893 //-----------------------------------------------------------------------------
00894 void SearchWindow::slotRedirectMsg()
00895 {
00896     KMCommand *command = new KMRedirectCommand(this, message());
00897     command->start();
00898 }
00899 
00900 //-----------------------------------------------------------------------------
00901 void SearchWindow::slotSaveMsg()
00902 {
00903     KMSaveMsgCommand *saveCommand = new KMSaveMsgCommand(this,
00904                                                          selectedMessages());
00905     if (saveCommand->url().isEmpty())
00906         delete saveCommand;
00907     else
00908         saveCommand->start();
00909 }
00910 //-----------------------------------------------------------------------------
00911 void SearchWindow::slotSaveAttachments()
00912 {
00913     KMSaveAttachmentsCommand *saveCommand = new KMSaveAttachmentsCommand(this,
00914                                                                          selectedMessages());
00915     saveCommand->start();
00916 }
00917 
00918 
00919 //-----------------------------------------------------------------------------
00920 void SearchWindow::slotPrintMsg()
00921 {
00922     KMCommand *command = new KMPrintCommand(this, message());
00923     command->start();
00924 }
00925 
00926 void SearchWindow::slotCopyMsgs()
00927 {
00928   QValueList<Q_UINT32> list = MessageCopyHelper::serNumListFromMsgList( selectedMessages() );
00929   mKMMainWidget->headers()->setCopiedMessages( list, false );
00930 }
00931 
00932 void SearchWindow::slotCutMsgs()
00933 {
00934   QValueList<Q_UINT32> list = MessageCopyHelper::serNumListFromMsgList( selectedMessages() );
00935   mKMMainWidget->headers()->setCopiedMessages( list, true );
00936 }
00937 
00938 
00939 void SearchWindow::setSearchPattern( const KMSearchPattern& pattern )
00940 {
00941     *mSearchPattern = pattern;
00942     mPatternEdit->setSearchPattern( mSearchPattern );
00943 }
00944 
00945 } // namespace KMail
00946 #include "searchwindow.moc"