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 <tdeapplication.h>
00039 #include <kdebug.h>
00040 #include <kstatusbar.h>
00041 #include <twin.h>
00042 #include <tdeconfig.h>
00043 #include <kstdaction.h>
00044 #include <kiconloader.h>
00045 
00046 #include <tqcheckbox.h>
00047 #include <tqlayout.h>
00048 #include <klineedit.h>
00049 #include <tqpushbutton.h>
00050 #include <tqradiobutton.h>
00051 #include <tqbuttongroup.h>
00052 #include <tqcombobox.h>
00053 #include <tqobjectlist.h> //for mPatternEdit->queryList( 0, "mRuleField" )->first();
00054 #include <tqcursor.h>
00055 #include <tqpopupmenu.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 // TDEListView sub-class for dnd support
00071 class MatchListView : public TDEListView
00072 {
00073   public:
00074     MatchListView( TQWidget *parent, SearchWindow* sw, const char* name = 0 ) :
00075       TDEListView( parent, name ),
00076       mSearchWindow( sw )
00077     {}
00078 
00079   protected:
00080     virtual TQDragObject* 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       TQPixmap pixmap;
00095       if( mailList.count() == 1 )
00096         pixmap = TQPixmap( DesktopIcon("message", TDEIcon::SizeSmall) );
00097       else
00098         pixmap = TQPixmap( DesktopIcon("application-vnd.tde.tdemultiple", TDEIcon::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"), "edit-find" ),
00114               KStdGuiItem::stop()),
00115   mStopped(false),
00116   mCloseRequested(false),
00117   mSortColumn(0),
00118   mSortOrder(Ascending),
00119   mFolder(0),
00120   mTimer(new TQTimer(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( tqt_xdisplay(), winId(), XA_WM_TRANSIENT_FOR );
00129 #endif
00130   KWin::setIcons(winId(), kapp->icon(), kapp->miniIcon());
00131 
00132   TDEConfig* config = KMKernel::config();
00133   config->setGroup("SearchDialog");
00134 
00135   TQWidget* searchWidget = new TQWidget(this);
00136   TQVBoxLayout *vbl = new TQVBoxLayout( searchWidget, 0, spacingHint(), "kmfs_vbl" );
00137 
00138   TQButtonGroup * radioGroup = new TQButtonGroup( searchWidget );
00139   radioGroup->hide();
00140 
00141   mChkbxAllFolders = new TQRadioButton(i18n("Search in &all local folders"), searchWidget);
00142   vbl->addWidget( mChkbxAllFolders );
00143   radioGroup->insert( mChkbxAllFolders );
00144 
00145   TQHBoxLayout *hbl = new TQHBoxLayout( vbl, spacingHint(), "kmfs_hbl" );
00146   mChkbxSpecificFolders = new TQRadioButton(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 TQCheckBox(i18n("I&nclude sub-folders"), searchWidget);
00158   mChkSubFolders->setChecked(true);
00159   hbl->addWidget(mChkSubFolders);
00160 
00161   TQWidget *spacer = new TQWidget( searchWidget, "spacer" );
00162   spacer->setMinimumHeight( 2 );
00163   vbl->addWidget( spacer );
00164 
00165   mPatternEdit = new KMSearchPatternEdit( "", searchWidget , "spe", false, true );
00166   mPatternEdit->setFrameStyle( TQFrame::NoFrame | TQFrame::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       TDEConfig 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   }
00185   mPatternEdit->setSearchPattern( mSearchPattern );
00186   TQObjectList *list = mPatternEdit->queryList( 0, "mRuleField" );
00187   TQObject *object = 0;
00188   if ( list )
00189       object = list->first();
00190   delete list;
00191   if (!searchFolder && object && ::tqqt_cast<TQComboBox*>(object))
00192       static_cast<TQComboBox*>(TQT_TQWIDGET(object))->setCurrentText("Subject");
00193 
00194   vbl->addWidget( mPatternEdit );
00195 
00196   // enable/disable widgets depending on radio buttons:
00197   connect( mChkbxSpecificFolders, TQT_SIGNAL(toggled(bool)),
00198            mCbxFolders, TQT_SLOT(setEnabled(bool)) );
00199   connect( mChkbxSpecificFolders, TQT_SIGNAL(toggled(bool)),
00200            mChkSubFolders, TQT_SLOT(setEnabled(bool)) );
00201   connect( mChkbxAllFolders, TQT_SIGNAL(toggled(bool)),
00202            TQT_TQOBJECT(this), TQT_SLOT(setEnabledSearchButton(bool)) );
00203 
00204   mLbxMatches = new MatchListView(searchWidget, this, "Find Messages");
00205 
00206   /*
00207      Default is to sort by date. TODO: Unfortunately this sorts *while*
00208      inserting, which looks rather strange - the user cannot read
00209      the results so far as they are constantly re-sorted --dnaber
00210 
00211      Sorting is now disabled when a search is started and reenabled
00212      when it stops. Items are appended to the list. This not only
00213      solves the above problem, but speeds searches with many hits
00214      up considerably. - till
00215 
00216      TODO: subclass TDEListViewItem and do proper (and performant)
00217      comapare functions
00218   */
00219   mLbxMatches->setSorting(2, false);
00220   mLbxMatches->setShowSortIndicator(true);
00221   mLbxMatches->setAllColumnsShowFocus(true);
00222   mLbxMatches->setSelectionModeExt(TDEListView::Extended);
00223   mLbxMatches->addColumn(i18n("Subject"),
00224                          config->readNumEntry("SubjectWidth", 150));
00225   mLbxMatches->addColumn(i18n("Sender/Receiver"),
00226                          config->readNumEntry("SenderWidth", 120));
00227   mLbxMatches->addColumn(i18n("Date"),
00228                          config->readNumEntry("DateWidth", 120));
00229   mLbxMatches->addColumn(i18n("Folder"),
00230                          config->readNumEntry("FolderWidth", 100));
00231 
00232   mLbxMatches->addColumn(""); // should be hidden
00233   mLbxMatches->setColumnWidthMode( MSGID_COLUMN, TQListView::Manual );
00234   mLbxMatches->setColumnWidth(MSGID_COLUMN, 0);
00235   mLbxMatches->header()->setResizeEnabled(false, MSGID_COLUMN);
00236 
00237   mLbxMatches->setDragEnabled( true );
00238 
00239   connect( mLbxMatches, TQT_SIGNAL(clicked(TQListViewItem *)),
00240            TQT_TQOBJECT(this), TQT_SLOT(slotShowMsg(TQListViewItem *)) );
00241   connect( mLbxMatches, TQT_SIGNAL(doubleClicked(TQListViewItem *)),
00242            TQT_TQOBJECT(this), TQT_SLOT(slotViewMsg(TQListViewItem *)) );
00243   connect( mLbxMatches, TQT_SIGNAL(currentChanged(TQListViewItem *)),
00244            TQT_TQOBJECT(this), TQT_SLOT(slotCurrentChanged(TQListViewItem *)) );
00245   connect( mLbxMatches, TQT_SIGNAL(contextMenuRequested(TQListViewItem *,const TQPoint &,int)),
00246            TQT_TQOBJECT(this), TQT_SLOT(slotContextMenuRequested(TQListViewItem *,const TQPoint &,int)) );
00247   vbl->addWidget( mLbxMatches );
00248 
00249   TQHBoxLayout *hbl2 = new TQHBoxLayout( vbl, spacingHint(), "kmfs_hbl2" );
00250   mSearchFolderLbl = new TQLabel(i18n("Search folder &name:"), searchWidget);
00251   hbl2->addWidget(mSearchFolderLbl);
00252   mSearchFolderEdt = new KLineEdit(searchWidget);
00253   if (searchFolder)
00254     mSearchFolderEdt->setText(searchFolder->folder()->name());
00255   else
00256     mSearchFolderEdt->setText(i18n("Last Search"));
00257 
00258   mSearchFolderLbl->setBuddy(mSearchFolderEdt);
00259   hbl2->addWidget(mSearchFolderEdt);
00260   mSearchFolderOpenBtn = new TQPushButton(i18n("Op&en Search Folder"), searchWidget);
00261   mSearchFolderOpenBtn->setEnabled(false);
00262   hbl2->addWidget(mSearchFolderOpenBtn);
00263   connect( mSearchFolderEdt, TQT_SIGNAL( textChanged( const TQString &)),
00264            TQT_TQOBJECT(this), TQT_SLOT( scheduleRename( const TQString & )));
00265   connect( &mRenameTimer, TQT_SIGNAL( timeout() ),
00266            TQT_TQOBJECT(this), TQT_SLOT( renameSearchFolder() ));
00267   connect( mSearchFolderOpenBtn, TQT_SIGNAL( clicked() ),
00268            TQT_TQOBJECT(this), TQT_SLOT( openSearchFolder() ));
00269   mSearchResultOpenBtn = new TQPushButton(i18n("Open &Message"), searchWidget);
00270   mSearchResultOpenBtn->setEnabled(false);
00271   hbl2->addWidget(mSearchResultOpenBtn);
00272   connect( mSearchResultOpenBtn, TQT_SIGNAL( clicked() ),
00273            TQT_TQOBJECT(this), TQT_SLOT( slotViewSelectedMsg() ));
00274   mStatusBar = new KStatusBar(searchWidget);
00275   mStatusBar->insertFixedItem(i18n("AMiddleLengthText..."), 0, true);
00276   mStatusBar->changeItem(i18n("Ready."), 0);
00277   mStatusBar->setItemAlignment(0, AlignLeft | AlignVCenter);
00278   mStatusBar->insertItem(TQString(), 1, 1, true);
00279   mStatusBar->setItemAlignment(1, AlignLeft | AlignVCenter);
00280   vbl->addWidget(mStatusBar);
00281 
00282   int mainWidth = config->readNumEntry("SearchWidgetWidth", 0);
00283   int mainHeight = config->readNumEntry("SearchWidgetHeight", 0);
00284 
00285   if (mainWidth || mainHeight)
00286     resize(mainWidth, mainHeight);
00287 
00288   setMainWidget(searchWidget);
00289   setButtonBoxOrientation(Qt::Vertical);
00290 
00291   mBtnSearch = actionButton(KDialogBase::User1);
00292   mBtnStop = actionButton(KDialogBase::User2);
00293   mBtnStop->setEnabled(false);
00294 
00295   connect(this, TQT_SIGNAL(user1Clicked()), TQT_SLOT(slotSearch()));
00296   connect(this, TQT_SIGNAL(user2Clicked()), TQT_SLOT(slotStop()));
00297   connect(this, TQT_SIGNAL(finished()), TQT_TQOBJECT(this), TQT_SLOT(deleteLater()));
00298 
00299   // give focus to the value field of the first search rule
00300   object = mPatternEdit->child( "regExpLineEdit" );
00301   if ( object && object->isWidgetType() ) {
00302       TQT_TQWIDGET(object)->setFocus();
00303       //kdDebug(5006) << "SearchWindow: focus has been given to widget "
00304       //              << object->name() << endl;
00305   }
00306   else
00307       kdDebug(5006) << "SearchWindow: regExpLineEdit not found" << endl;
00308 
00309   //set up actions
00310   TDEActionCollection *ac = actionCollection();
00311   ac->setWidget( this );
00312   mReplyAction = new TDEAction( i18n("&Reply..."), "mail-reply-sender", 0, TQT_TQOBJECT(this),
00313                               TQT_SLOT(slotReplyToMsg()), ac, "search_reply" );
00314   mReplyAllAction = new TDEAction( i18n("Reply to &All..."), "mail-reply-all",
00315                                  0, TQT_TQOBJECT(this), TQT_SLOT(slotReplyAllToMsg()),
00316                                  ac, "search_reply_all" );
00317   mReplyListAction = new TDEAction( i18n("Reply to Mailing-&List..."),
00318                                   "mail_replylist", 0, TQT_TQOBJECT(this),
00319                                   TQT_SLOT(slotReplyListToMsg()), ac,
00320                                   "search_reply_list" );
00321   mForwardActionMenu = new TDEActionMenu( i18n("Message->","&Forward"),
00322                                         "mail-forward", ac,
00323                                         "search_message_forward" );
00324   connect( mForwardActionMenu, TQT_SIGNAL(activated()), this,
00325            TQT_SLOT(slotForwardInlineMsg()) );
00326   mForwardAttachedAction = new TDEAction( i18n("Message->Forward->","As &Attachment..."),
00327                                         "mail-forward", 0, TQT_TQOBJECT(this),
00328                                         TQT_SLOT(slotForwardAttachedMsg()), ac,
00329                                         "search_message_forward_as_attachment" );
00330   mForwardInlineAction = new TDEAction( i18n("&Inline..."),
00331                                       "mail-forward", 0, TQT_TQOBJECT(this),
00332                                       TQT_SLOT(slotForwardInlineMsg()), ac,
00333                                       "search_message_forward_inline" );
00334   if ( GlobalSettings::self()->forwardingInlineByDefault() ) {
00335     mForwardActionMenu->insert( mForwardInlineAction );
00336     mForwardActionMenu->insert( mForwardAttachedAction );
00337   } else {
00338     mForwardActionMenu->insert( mForwardAttachedAction );
00339     mForwardActionMenu->insert( mForwardInlineAction );
00340   }
00341 
00342   mForwardDigestAction = new TDEAction( i18n("Message->Forward->","As Di&gest..."),
00343                                       "mail-forward", 0, TQT_TQOBJECT(this),
00344                                       TQT_SLOT(slotForwardDigestMsg()), ac,
00345                                       "search_message_forward_as_digest" );
00346   mForwardActionMenu->insert( mForwardDigestAction );
00347   mRedirectAction = new TDEAction( i18n("Message->Forward->","&Redirect..."),
00348                                       "mail-forward", 0, TQT_TQOBJECT(this),
00349                                       TQT_SLOT(slotRedirectMsg()), ac,
00350                                       "search_message_forward_redirect" );
00351   mForwardActionMenu->insert( mRedirectAction );
00352   mSaveAsAction = KStdAction::saveAs( TQT_TQOBJECT(this), TQT_SLOT(slotSaveMsg()), ac, "search_file_save_as" );
00353   mSaveAtchAction = new TDEAction( i18n("Save Attachments..."), "attach", 0,
00354                                  TQT_TQOBJECT(this), TQT_SLOT(slotSaveAttachments()), ac, "search_save_attachments" );
00355 
00356   mPrintAction = KStdAction::print( TQT_TQOBJECT(this), TQT_SLOT(slotPrintMsg()), ac, "search_print" );
00357   mClearAction = new TDEAction( i18n("Clear Selection"), 0, 0, TQT_TQOBJECT(this),
00358                               TQT_SLOT(slotClearSelection()), ac, "search_clear_selection" );
00359 
00360   mCopyAction = KStdAction::copy( TQT_TQOBJECT(this), TQT_SLOT(slotCopyMsgs()), ac, "search_copy_messages" );
00361   mCutAction = KStdAction::cut( TQT_TQOBJECT(this), TQT_SLOT(slotCutMsgs()), ac, "search_cut_messages" );
00362 
00363   connect(mTimer, TQT_SIGNAL(timeout()), TQT_TQOBJECT(this), TQT_SLOT(updStatus()));
00364   connect(kmkernel->searchFolderMgr(), TQT_SIGNAL(folderInvalidated(KMFolder*)),
00365           TQT_TQOBJECT(this), TQT_SLOT(folderInvalidated(KMFolder*)));
00366 
00367   connect(mCbxFolders, TQT_SIGNAL(folderChanged(KMFolder*)),
00368           TQT_TQOBJECT(this), TQT_SLOT(slotFolderActivated()));
00369 
00370 }
00371 
00372 //-----------------------------------------------------------------------------
00373 SearchWindow::~SearchWindow()
00374 {
00375   TQValueListIterator<TQGuardedPtr<KMFolder> > fit;
00376   for ( fit = mFolders.begin(); fit != mFolders.end(); ++fit ) {
00377     if (!(*fit))
00378       continue;
00379     (*fit)->close("searchwindow");
00380   }
00381 
00382   TDEConfig* config = KMKernel::config();
00383   config->setGroup("SearchDialog");
00384   config->writeEntry("SubjectWidth", mLbxMatches->columnWidth(0));
00385   config->writeEntry("SenderWidth", mLbxMatches->columnWidth(1));
00386   config->writeEntry("DateWidth", mLbxMatches->columnWidth(2));
00387   config->writeEntry("FolderWidth", mLbxMatches->columnWidth(3));
00388   config->writeEntry("SearchWidgetWidth", width());
00389   config->writeEntry("SearchWidgetHeight", height());
00390   config->sync();
00391 }
00392 
00393 void SearchWindow::setEnabledSearchButton(bool)
00394 {
00395   //Make sure that button is enable
00396   //Before when we selected a folder == "Local Folder" as that it was not a folder
00397   //search button was disable, and when we select "Search in all local folder"
00398   //Search button was never enabled :(
00399   mBtnSearch->setEnabled( true );
00400 }
00401 
00402 //-----------------------------------------------------------------------------
00403 void SearchWindow::updStatus(void)
00404 {
00405     TQString genMsg, detailMsg, procMsg;
00406     int numMatches = 0, numProcessed = 0;
00407     KMSearch const *search = (mFolder) ? (mFolder->search()) : 0;
00408     TQString folderName;
00409     if (search) {
00410         numMatches = search->foundCount();
00411         numProcessed = search->searchCount();
00412         folderName = search->currentFolder();
00413     }
00414 
00415     if (search && !search->running()) {
00416         procMsg = i18n("%n message searched", "%n messages searched",
00417                        numProcessed);
00418         if(!mStopped) {
00419             genMsg = i18n("Done.");
00420             detailMsg = i18n("%n match in %1", "%n matches in %1",
00421                              numMatches).arg(procMsg);
00422         } else {
00423             genMsg = i18n("Search canceled.");
00424             detailMsg = i18n("%n match so far in %1", "%n matches so far in %1",
00425                              numMatches).arg(procMsg);
00426         }
00427     } else {
00428         procMsg = i18n("%n message", "%n messages", numProcessed);
00429         genMsg = i18n("%n match", "%n matches", numMatches);
00430         detailMsg = i18n("Searching in %1. %2 searched so far")
00431                     .arg(folderName).arg(procMsg);
00432     }
00433 
00434     mStatusBar->changeItem(genMsg, 0);
00435     mStatusBar->changeItem(detailMsg, 1);
00436 }
00437 
00438 
00439 //-----------------------------------------------------------------------------
00440 void SearchWindow::keyPressEvent(TQKeyEvent *evt)
00441 {
00442     KMSearch const *search = (mFolder) ? mFolder->search() : 0;
00443     bool searching = (search) ? search->running() : false;
00444     if (evt->key() == Key_Escape && searching) {
00445         mFolder->stopSearch();
00446         return;
00447     }
00448 
00449     KDialogBase::keyPressEvent(evt);
00450 }
00451 
00452 
00453 //-----------------------------------------------------------------------------
00454 void SearchWindow::slotFolderActivated()
00455 {
00456     mChkbxSpecificFolders->setChecked(true);
00457 }
00458 
00459 //-----------------------------------------------------------------------------
00460 void SearchWindow::activateFolder(KMFolder *curFolder)
00461 {
00462     mChkbxSpecificFolders->setChecked(true);
00463     mCbxFolders->setFolder(curFolder);
00464 }
00465 
00466 //-----------------------------------------------------------------------------
00467 void SearchWindow::slotSearch()
00468 {
00469     mLastFocus = focusWidget();
00470     mBtnSearch->setFocus();     // set focus so we don't miss key event
00471 
00472     mStopped = false;
00473     mFetchingInProgress = 0;
00474 
00475     mSearchFolderOpenBtn->setEnabled(true);
00476     if ( mSearchFolderEdt->text().isEmpty() ) {
00477       mSearchFolderEdt->setText( i18n("Last Search") );
00478     }
00479     mBtnSearch->setEnabled(false);
00480     mBtnStop->setEnabled(true);
00481 
00482     mLbxMatches->clear();
00483 
00484     mSortColumn = mLbxMatches->sortColumn();
00485     mSortOrder = mLbxMatches->sortOrder();
00486     mLbxMatches->setSorting(-1);
00487     mLbxMatches->setShowSortIndicator(false);
00488 
00489     // If we haven't openend an existing search folder, find or
00490     // create one.
00491     if (!mFolder) {
00492       KMFolderMgr *mgr = kmkernel->searchFolderMgr();
00493       TQString baseName = mSearchFolderEdt->text();
00494       TQString fullName = baseName;
00495       int count = 0;
00496       KMFolder *folder;
00497       while ((folder = mgr->find(fullName))) {
00498         if (folder->storage()->inherits("KMFolderSearch"))
00499           break;
00500         fullName = TQString("%1 %2").arg(baseName).arg(++count);
00501       }
00502 
00503       if (!folder)
00504         folder = mgr->createFolder(fullName, false, KMFolderTypeSearch,
00505             &mgr->dir());
00506 
00507       mFolder = dynamic_cast<KMFolderSearch*>( folder->storage() );
00508     }
00509     mFolder->stopSearch();
00510     disconnect(mFolder, TQT_SIGNAL(msgAdded(int)),
00511             TQT_TQOBJECT(this), TQT_SLOT(slotAddMsg(int)));
00512     disconnect(mFolder, TQT_SIGNAL(msgRemoved(KMFolder*, TQ_UINT32)),
00513             TQT_TQOBJECT(this), TQT_SLOT(slotRemoveMsg(KMFolder*, TQ_UINT32)));
00514     connect(mFolder, TQT_SIGNAL(msgAdded(int)),
00515             TQT_TQOBJECT(this), TQT_SLOT(slotAddMsg(int)));
00516     connect(mFolder, TQT_SIGNAL(msgRemoved(KMFolder*, TQ_UINT32)),
00517             TQT_TQOBJECT(this), TQT_SLOT(slotRemoveMsg(KMFolder*, TQ_UINT32)));
00518     mSearchFolderEdt->setEnabled(false);
00519     KMSearch *search = new KMSearch();
00520     connect(search, TQT_SIGNAL(finished(bool)),
00521             TQT_TQOBJECT(this), TQT_SLOT(searchDone()));
00522     if (mChkbxAllFolders->isChecked()) {
00523         search->setRecursive(true);
00524     } else {
00525         search->setRoot(mCbxFolders->folder());
00526         search->setRecursive(mChkSubFolders->isChecked());
00527     }
00528 
00529     mPatternEdit->updateSearchPattern();
00530     KMSearchPattern *searchPattern = new KMSearchPattern();
00531     *searchPattern = *mSearchPattern; //deep copy
00532     searchPattern->purify();
00533     search->setSearchPattern(searchPattern);
00534     mFolder->setSearch(search);
00535     enableGUI();
00536 
00537     mTimer->start(200);
00538 }
00539 
00540 //-----------------------------------------------------------------------------
00541 void SearchWindow::searchDone()
00542 {
00543     mTimer->stop();
00544     updStatus();
00545 
00546     TQTimer::singleShot(0, TQT_TQOBJECT(this), TQT_SLOT(enableGUI()));
00547     if(mLastFocus)
00548         mLastFocus->setFocus();
00549     if (mCloseRequested)
00550         close();
00551 
00552     mLbxMatches->setSorting(mSortColumn, mSortOrder == Ascending);
00553     mLbxMatches->setShowSortIndicator(true);
00554 
00555     mSearchFolderEdt->setEnabled(true);
00556 }
00557 
00558 void SearchWindow::slotAddMsg(int idx)
00559 {
00560     if (!mFolder)
00561         return;
00562     bool unget = !mFolder->isMessage(idx);
00563     KMMessage *msg = mFolder->getMsg(idx);
00564     TQString from, fName;
00565     KMFolder *pFolder = msg->parent();
00566     if (!mFolders.contains(pFolder)) {
00567         mFolders.append(pFolder);
00568         pFolder->open("searchwindow");
00569     }
00570     if(pFolder->whoField() == "To")
00571         from = msg->to();
00572     else
00573         from = msg->from();
00574     if (pFolder->isSystemFolder())
00575         fName = i18n(pFolder->name().utf8());
00576     else
00577         fName = pFolder->name();
00578 
00579     (void)new TDEListViewItem(mLbxMatches, mLbxMatches->lastItem(),
00580                             msg->subject(), from, msg->dateIsoStr(),
00581                             fName,
00582                             TQString::number(mFolder->serNum(idx)));
00583     if (unget)
00584         mFolder->unGetMsg(idx);
00585 }
00586 
00587 void SearchWindow::slotRemoveMsg(KMFolder *, TQ_UINT32 serNum)
00588 {
00589     if (!mFolder)
00590         return;
00591     TQListViewItemIterator it(mLbxMatches);
00592     while (it.current()) {
00593         TQListViewItem *item = *it;
00594         if (serNum == (*it)->text(MSGID_COLUMN).toUInt()) {
00595             delete item;
00596             return;
00597         }
00598         ++it;
00599     }
00600 }
00601 
00602 //-----------------------------------------------------------------------------
00603 void SearchWindow::slotStop()
00604 {
00605     if (mFolder)
00606       mFolder->stopSearch();
00607     mStopped = true;
00608     mBtnStop->setEnabled(false);
00609 }
00610 
00611 //-----------------------------------------------------------------------------
00612 void SearchWindow::slotClose()
00613 {
00614     accept();
00615 }
00616 
00617 
00618 //-----------------------------------------------------------------------------
00619 void SearchWindow::closeEvent(TQCloseEvent *e)
00620 {
00621     if (mFolder && mFolder->search() && mFolder->search()->running()) {
00622       mCloseRequested = true;
00623       //Cancel search in progress by setting the search folder search to
00624       //the null search
00625       mFolder->setSearch(new KMSearch());
00626       TQTimer::singleShot(0, TQT_TQOBJECT(this), TQT_SLOT(slotClose()));
00627     } else {
00628       KDialogBase::closeEvent(e);
00629     }
00630 }
00631 
00632 //-----------------------------------------------------------------------------
00633 void SearchWindow::scheduleRename( const TQString &s)
00634 {
00635     if (!s.isEmpty() ) {
00636       mRenameTimer.start(250, true);
00637       mSearchFolderOpenBtn->setEnabled(false);
00638     } else {
00639       mRenameTimer.stop();
00640       mSearchFolderOpenBtn->setEnabled(!s.isEmpty());
00641     }
00642 }
00643 
00644 //-----------------------------------------------------------------------------
00645 void SearchWindow::renameSearchFolder()
00646 {
00647     if (mFolder && (mFolder->folder()->name() != mSearchFolderEdt->text())) {
00648         int i = 1;
00649         TQString name =  mSearchFolderEdt->text();
00650         while (i < 100) {
00651             if (!kmkernel->searchFolderMgr()->find( name )) {
00652                 mFolder->rename( name );
00653                 kmkernel->searchFolderMgr()->contentsChanged();
00654                 break;
00655             }
00656             name.setNum( i );
00657             name = mSearchFolderEdt->text() + " " + name;
00658             ++i;
00659         }
00660     }
00661     if ( mFolder )
00662       mSearchFolderOpenBtn->setEnabled(true);
00663 }
00664 
00665 void SearchWindow::openSearchFolder()
00666 {
00667   Q_ASSERT( mFolder );
00668     renameSearchFolder();
00669     mKMMainWidget->slotSelectFolder( mFolder->folder() );
00670     slotClose();
00671 }
00672 
00673 //-----------------------------------------------------------------------------
00674 void SearchWindow::folderInvalidated(KMFolder *folder)
00675 {
00676     if (folder->storage() == mFolder) {
00677         mLbxMatches->clear();
00678         if (mFolder->search())
00679             connect(mFolder->search(), TQT_SIGNAL(finished(bool)),
00680                     TQT_TQOBJECT(this), TQT_SLOT(searchDone()));
00681         mTimer->start(200);
00682         enableGUI();
00683     }
00684 }
00685 
00686 //-----------------------------------------------------------------------------
00687 KMMessage *SearchWindow::indexToMessage( TQListViewItem *item )
00688 {
00689   if( !item ) {
00690     return 0;
00691   }
00692 
00693   KMFolder *folder;
00694   int msgIndex;
00695   KMMsgDict::instance()->getLocation( item->text( MSGID_COLUMN ).toUInt(),
00696                                       &folder, &msgIndex );
00697 
00698   if ( !folder || msgIndex < 0 ) {
00699     return 0;
00700   }
00701 
00702   mKMMainWidget->slotSelectFolder( folder );
00703   return folder->getMsg( msgIndex );
00704 }
00705 
00706 //-----------------------------------------------------------------------------
00707 bool SearchWindow::slotShowMsg( TQListViewItem *item )
00708 {
00709   KMMessage *message = indexToMessage( item );
00710   if ( message ) {
00711     mKMMainWidget->slotSelectMessage( message );
00712     return true;
00713   }
00714   return false;
00715 }
00716 
00717 //-----------------------------------------------------------------------------
00718 void SearchWindow::slotViewSelectedMsg()
00719 {
00720   slotViewMsg( mLbxMatches->currentItem() );
00721 }
00722 
00723 //-----------------------------------------------------------------------------
00724 bool SearchWindow::slotViewMsg( TQListViewItem *item )
00725 {
00726   KMMessage *message = indexToMessage( item );
00727   if ( message ) {
00728     mKMMainWidget->slotMsgActivated( message );
00729     return true;
00730   }
00731   return false;
00732 }
00733 
00734 //-----------------------------------------------------------------------------
00735 void SearchWindow::slotCurrentChanged(TQListViewItem *item)
00736 {
00737   mSearchResultOpenBtn->setEnabled(item!=0);
00738 }
00739 
00740 //-----------------------------------------------------------------------------
00741 void SearchWindow::enableGUI()
00742 {
00743     KMSearch const *search = (mFolder) ? (mFolder->search()) : 0;
00744     bool searching = (search) ? (search->running()) : false;
00745     actionButton(KDialogBase::Close)->setEnabled(!searching);
00746     mCbxFolders->setEnabled(!searching && !mChkbxAllFolders->isChecked());
00747     mChkSubFolders->setEnabled(!searching && !mChkbxAllFolders->isChecked());
00748     mChkbxAllFolders->setEnabled(!searching);
00749     mChkbxSpecificFolders->setEnabled(!searching);
00750     mPatternEdit->setEnabled(!searching);
00751     mBtnSearch->setEnabled(!searching);
00752     mBtnStop->setEnabled(searching);
00753 }
00754 
00755 
00756 //-----------------------------------------------------------------------------
00757 KMMessageList SearchWindow::selectedMessages()
00758 {
00759     KMMessageList msgList;
00760     KMFolder* folder = 0;
00761     int msgIndex = -1;
00762     for (TQListViewItemIterator it(mLbxMatches); it.current(); it++)
00763         if (it.current()->isSelected()) {
00764             KMMsgDict::instance()->getLocation((*it)->text(MSGID_COLUMN).toUInt(),
00765                                            &folder, &msgIndex);
00766             if (folder && msgIndex >= 0)
00767                 msgList.append(folder->getMsgBase(msgIndex));
00768         }
00769     return msgList;
00770 }
00771 
00772 //-----------------------------------------------------------------------------
00773 KMMessage* SearchWindow::message()
00774 {
00775     TQListViewItem *item = mLbxMatches->currentItem();
00776     KMFolder* folder = 0;
00777     int msgIndex = -1;
00778     if (!item)
00779         return 0;
00780     KMMsgDict::instance()->getLocation(item->text(MSGID_COLUMN).toUInt(),
00781                                    &folder, &msgIndex);
00782     if (!folder || msgIndex < 0)
00783         return 0;
00784 
00785     return folder->getMsg(msgIndex);
00786 }
00787 
00788 //-----------------------------------------------------------------------------
00789 void SearchWindow::moveSelectedToFolder( int menuId )
00790 {
00791     KMFolder *dest = mMenuToFolder[menuId];
00792     if (!dest)
00793         return;
00794 
00795     KMMessageList msgList = selectedMessages();
00796     KMCommand *command = new KMMoveCommand( dest, msgList );
00797     command->start();
00798 }
00799 
00800 //-----------------------------------------------------------------------------
00801 void SearchWindow::copySelectedToFolder( int menuId )
00802 {
00803     KMFolder *dest = mMenuToFolder[menuId];
00804     if (!dest)
00805         return;
00806 
00807     KMMessageList msgList = selectedMessages();
00808     KMCommand *command = new KMCopyCommand( dest, msgList );
00809     command->start();
00810 }
00811 
00812 //-----------------------------------------------------------------------------
00813 void SearchWindow::updateContextMenuActions()
00814 {
00815     int count = selectedMessages().count();
00816     bool single_actions = count == 1;
00817     mReplyAction->setEnabled( single_actions );
00818     mReplyAllAction->setEnabled( single_actions );
00819     mReplyListAction->setEnabled( single_actions );
00820     mPrintAction->setEnabled( single_actions );
00821     mForwardDigestAction->setEnabled( !single_actions );
00822     mRedirectAction->setEnabled( single_actions );
00823     mCopyAction->setEnabled( count > 0 );
00824     mCutAction->setEnabled( count > 0 );
00825 }
00826 
00827 //-----------------------------------------------------------------------------
00828 void SearchWindow::slotContextMenuRequested( TQListViewItem *lvi, const TQPoint &, int )
00829 {
00830     if (!lvi)
00831         return;
00832     mLbxMatches->setSelected( lvi, true );
00833     mLbxMatches->setCurrentItem( lvi );
00834     // FIXME is this ever unGetMsg()'d?
00835     if (!message())
00836         return;
00837     TQPopupMenu *menu = new TQPopupMenu(this);
00838     updateContextMenuActions();
00839 
00840     mMenuToFolder.clear();
00841     TQPopupMenu *msgMoveMenu = new TQPopupMenu(menu);
00842     mKMMainWidget->folderTree()->folderToPopupMenu( KMFolderTree::MoveMessage,
00843         TQT_TQOBJECT(this), &mMenuToFolder, msgMoveMenu );
00844     TQPopupMenu *msgCopyMenu = new TQPopupMenu(menu);
00845     mKMMainWidget->folderTree()->folderToPopupMenu( KMFolderTree::CopyMessage,
00846         TQT_TQOBJECT(this), &mMenuToFolder, msgCopyMenu );
00847 
00848     // show most used actions
00849     mReplyAction->plug(menu);
00850     mReplyAllAction->plug(menu);
00851     mReplyListAction->plug(menu);
00852     mForwardActionMenu->plug(menu);
00853     menu->insertSeparator();
00854     mCopyAction->plug(menu);
00855     mCutAction->plug(menu);
00856     menu->insertItem(i18n("&Copy To"), msgCopyMenu);
00857     menu->insertItem(i18n("&Move To"), msgMoveMenu);
00858     menu->insertSeparator();
00859     mSaveAsAction->plug(menu);
00860     mSaveAtchAction->plug(menu);
00861     mPrintAction->plug(menu);
00862     menu->insertSeparator();
00863     mClearAction->plug(menu);
00864     menu->exec (TQCursor::pos(), 0);
00865     delete menu;
00866 }
00867 
00868 //-----------------------------------------------------------------------------
00869 void SearchWindow::slotClearSelection()
00870 {
00871     mLbxMatches->clearSelection();
00872 }
00873 
00874 //-----------------------------------------------------------------------------
00875 void SearchWindow::slotReplyToMsg()
00876 {
00877     KMCommand *command = new KMReplyToCommand(this, message());
00878     command->start();
00879 }
00880 
00881 //-----------------------------------------------------------------------------
00882 void SearchWindow::slotReplyAllToMsg()
00883 {
00884     KMCommand *command = new KMReplyToAllCommand(this, message());
00885     command->start();
00886 }
00887 
00888 //-----------------------------------------------------------------------------
00889 void SearchWindow::slotReplyListToMsg()
00890 {
00891     KMCommand *command = new KMReplyListCommand(this, message());
00892     command->start();
00893 }
00894 
00895 //-----------------------------------------------------------------------------
00896 void SearchWindow::slotForwardInlineMsg()
00897 {
00898     KMCommand *command = new KMForwardInlineCommand(this, selectedMessages());
00899     command->start();
00900 }
00901 
00902 //-----------------------------------------------------------------------------
00903 void SearchWindow::slotForwardAttachedMsg()
00904 {
00905     KMCommand *command = new KMForwardAttachedCommand(this, selectedMessages());
00906     command->start();
00907 }
00908 
00909 //-----------------------------------------------------------------------------
00910 void SearchWindow::slotForwardDigestMsg()
00911 {
00912     KMCommand *command = new KMForwardDigestCommand(this, selectedMessages());
00913     command->start();
00914 }
00915 
00916 //-----------------------------------------------------------------------------
00917 void SearchWindow::slotRedirectMsg()
00918 {
00919     KMCommand *command = new KMRedirectCommand(this, message());
00920     command->start();
00921 }
00922 
00923 //-----------------------------------------------------------------------------
00924 void SearchWindow::slotSaveMsg()
00925 {
00926     KMSaveMsgCommand *saveCommand = new KMSaveMsgCommand(this,
00927                                                          selectedMessages());
00928     if (saveCommand->url().isEmpty())
00929         delete saveCommand;
00930     else
00931         saveCommand->start();
00932 }
00933 //-----------------------------------------------------------------------------
00934 void SearchWindow::slotSaveAttachments()
00935 {
00936     KMSaveAttachmentsCommand *saveCommand = new KMSaveAttachmentsCommand(this,
00937                                                                          selectedMessages());
00938     saveCommand->start();
00939 }
00940 
00941 
00942 //-----------------------------------------------------------------------------
00943 void SearchWindow::slotPrintMsg()
00944 {
00945     KMCommand *command = new KMPrintCommand(this, message());
00946     command->start();
00947 }
00948 
00949 void SearchWindow::slotCopyMsgs()
00950 {
00951   TQValueList<TQ_UINT32> list = MessageCopyHelper::serNumListFromMsgList( selectedMessages() );
00952   mKMMainWidget->headers()->setCopiedMessages( list, false );
00953 }
00954 
00955 void SearchWindow::slotCutMsgs()
00956 {
00957   TQValueList<TQ_UINT32> list = MessageCopyHelper::serNumListFromMsgList( selectedMessages() );
00958   mKMMainWidget->headers()->setCopiedMessages( list, true );
00959 }
00960 
00961 
00962 void SearchWindow::setSearchPattern( const KMSearchPattern& pattern )
00963 {
00964     *mSearchPattern = pattern;
00965     mPatternEdit->setSearchPattern( mSearchPattern );
00966 }
00967 
00968 } // namespace KMail
00969 #include "searchwindow.moc"