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   }
00185   mPatternEdit->setSearchPattern( mSearchPattern );
00186   QObjectList *list = mPatternEdit->queryList( 0, "mRuleField" );
00187   QObject *object = 0;
00188   if ( list )
00189       object = list->first();
00190   delete list;
00191   if (!searchFolder && object && ::qt_cast<QComboBox*>(object))
00192       static_cast<QComboBox*>(object)->setCurrentText("Subject");
00193 
00194   vbl->addWidget( mPatternEdit );
00195 
00196   // enable/disable widgets depending on radio buttons:
00197   connect( mChkbxSpecificFolders, SIGNAL(toggled(bool)),
00198            mCbxFolders, SLOT(setEnabled(bool)) );
00199   connect( mChkbxSpecificFolders, SIGNAL(toggled(bool)),
00200            mChkSubFolders, SLOT(setEnabled(bool)) );
00201   connect( mChkbxAllFolders, SIGNAL(toggled(bool)),
00202            this, 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 KListViewItem 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(KListView::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, QListView::Manual );
00234   mLbxMatches->setColumnWidth(MSGID_COLUMN, 0);
00235   mLbxMatches->header()->setResizeEnabled(false, MSGID_COLUMN);
00236 
00237   mLbxMatches->setDragEnabled( true );
00238 
00239   connect( mLbxMatches, SIGNAL(clicked(QListViewItem *)),
00240            this, SLOT(slotShowMsg(QListViewItem *)) );
00241   connect( mLbxMatches, SIGNAL(doubleClicked(QListViewItem *)),
00242            this, SLOT(slotViewMsg(QListViewItem *)) );
00243   connect( mLbxMatches, SIGNAL(currentChanged(QListViewItem *)),
00244            this, SLOT(slotCurrentChanged(QListViewItem *)) );
00245   connect( mLbxMatches, SIGNAL(contextMenuRequested(QListViewItem *,const QPoint &,int)),
00246            this, SLOT(slotContextMenuRequested(QListViewItem *,const QPoint &,int)) );
00247   vbl->addWidget( mLbxMatches );
00248 
00249   QHBoxLayout *hbl2 = new QHBoxLayout( vbl, spacingHint(), "kmfs_hbl2" );
00250   mSearchFolderLbl = new QLabel(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 QPushButton(i18n("Op&en Search Folder"), searchWidget);
00261   mSearchFolderOpenBtn->setEnabled(false);
00262   hbl2->addWidget(mSearchFolderOpenBtn);
00263   connect( mSearchFolderEdt, SIGNAL( textChanged( const QString &)),
00264            this, SLOT( scheduleRename( const QString & )));
00265   connect( &mRenameTimer, SIGNAL( timeout() ),
00266            this, SLOT( renameSearchFolder() ));
00267   connect( mSearchFolderOpenBtn, SIGNAL( clicked() ),
00268            this, SLOT( openSearchFolder() ));
00269   mSearchResultOpenBtn = new QPushButton(i18n("Open &Message"), searchWidget);
00270   mSearchResultOpenBtn->setEnabled(false);
00271   hbl2->addWidget(mSearchResultOpenBtn);
00272   connect( mSearchResultOpenBtn, SIGNAL( clicked() ),
00273            this, 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(QString::null, 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(QWidget::Vertical);
00290 
00291   mBtnSearch = actionButton(KDialogBase::User1);
00292   mBtnStop = actionButton(KDialogBase::User2);
00293   mBtnStop->setEnabled(false);
00294 
00295   connect(this, SIGNAL(user1Clicked()), SLOT(slotSearch()));
00296   connect(this, SIGNAL(user2Clicked()), SLOT(slotStop()));
00297   connect(this, SIGNAL(finished()), this, 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       static_cast<QWidget*>(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   KActionCollection *ac = actionCollection();
00311   ac->setWidget( this );
00312   mReplyAction = new KAction( i18n("&Reply..."), "mail_reply", 0, this,
00313                               SLOT(slotReplyToMsg()), ac, "search_reply" );
00314   mReplyAllAction = new KAction( i18n("Reply to &All..."), "mail_replyall",
00315                                  0, this, SLOT(slotReplyAllToMsg()),
00316                                  ac, "search_reply_all" );
00317   mReplyListAction = new KAction( i18n("Reply to Mailing-&List..."),
00318                                   "mail_replylist", 0, this,
00319                                   SLOT(slotReplyListToMsg()), ac,
00320                                   "search_reply_list" );
00321   mForwardActionMenu = new KActionMenu( i18n("Message->","&Forward"),
00322                                         "mail_forward", ac,
00323                                         "search_message_forward" );
00324   connect( mForwardActionMenu, SIGNAL(activated()), this,
00325            SLOT(slotForwardInlineMsg()) );
00326   mForwardAttachedAction = new KAction( i18n("Message->Forward->","As &Attachment..."),
00327                                         "mail_forward", 0, this,
00328                                         SLOT(slotForwardAttachedMsg()), ac,
00329                                         "search_message_forward_as_attachment" );
00330   mForwardInlineAction = new KAction( i18n("&Inline..."),
00331                                       "mail_forward", 0, this,
00332                                       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 KAction( i18n("Message->Forward->","As Di&gest..."),
00343                                       "mail_forward", 0, this,
00344                                       SLOT(slotForwardDigestMsg()), ac,
00345                                       "search_message_forward_as_digest" );
00346   mForwardActionMenu->insert( mForwardDigestAction );
00347   mRedirectAction = new KAction( i18n("Message->Forward->","&Redirect..."),
00348                                       "mail_forward", 0, this,
00349                                       SLOT(slotRedirectMsg()), ac,
00350                                       "search_message_forward_redirect" );
00351   mForwardActionMenu->insert( mRedirectAction );
00352   mSaveAsAction = KStdAction::saveAs( this, SLOT(slotSaveMsg()), ac, "search_file_save_as" );
00353   mSaveAtchAction = new KAction( i18n("Save Attachments..."), "attach", 0,
00354                                  this, SLOT(slotSaveAttachments()), ac, "search_save_attachments" );
00355 
00356   mPrintAction = KStdAction::print( this, SLOT(slotPrintMsg()), ac, "search_print" );
00357   mClearAction = new KAction( i18n("Clear Selection"), 0, 0, this,
00358                               SLOT(slotClearSelection()), ac, "search_clear_selection" );
00359 
00360   mCopyAction = KStdAction::copy( this, SLOT(slotCopyMsgs()), ac, "search_copy_messages" );
00361   mCutAction = KStdAction::cut( this, SLOT(slotCutMsgs()), ac, "search_cut_messages" );
00362 
00363   connect(mTimer, SIGNAL(timeout()), this, SLOT(updStatus()));
00364   connect(kmkernel->searchFolderMgr(), SIGNAL(folderInvalidated(KMFolder*)),
00365           this, SLOT(folderInvalidated(KMFolder*)));
00366 
00367   connect(mCbxFolders, SIGNAL(folderChanged(KMFolder*)),
00368           this, SLOT(slotFolderActivated()));
00369 
00370 }
00371 
00372 //-----------------------------------------------------------------------------
00373 SearchWindow::~SearchWindow()
00374 {
00375   QValueListIterator<QGuardedPtr<KMFolder> > fit;
00376   for ( fit = mFolders.begin(); fit != mFolders.end(); ++fit ) {
00377     if (!(*fit))
00378       continue;
00379     (*fit)->close("searchwindow");
00380   }
00381 
00382   KConfig* 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     QString genMsg, detailMsg, procMsg;
00406     int numMatches = 0, numProcessed = 0;
00407     KMSearch const *search = (mFolder) ? (mFolder->search()) : 0;
00408     QString 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(QKeyEvent *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       QString baseName = mSearchFolderEdt->text();
00494       QString 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 = QString("%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, SIGNAL(msgAdded(int)),
00511             this, SLOT(slotAddMsg(int)));
00512     disconnect(mFolder, SIGNAL(msgRemoved(KMFolder*, Q_UINT32)),
00513             this, SLOT(slotRemoveMsg(KMFolder*, Q_UINT32)));
00514     connect(mFolder, SIGNAL(msgAdded(int)),
00515             this, SLOT(slotAddMsg(int)));
00516     connect(mFolder, SIGNAL(msgRemoved(KMFolder*, Q_UINT32)),
00517             this, SLOT(slotRemoveMsg(KMFolder*, Q_UINT32)));
00518     mSearchFolderEdt->setEnabled(false);
00519     KMSearch *search = new KMSearch();
00520     connect(search, SIGNAL(finished(bool)),
00521             this, 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     QTimer::singleShot(0, this, 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     QString 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 KListViewItem(mLbxMatches, mLbxMatches->lastItem(),
00580                             msg->subject(), from, msg->dateIsoStr(),
00581                             fName,
00582                             QString::number(mFolder->serNum(idx)));
00583     if (unget)
00584         mFolder->unGetMsg(idx);
00585 }
00586 
00587 void SearchWindow::slotRemoveMsg(KMFolder *, Q_UINT32 serNum)
00588 {
00589     if (!mFolder)
00590         return;
00591     QListViewItemIterator it(mLbxMatches);
00592     while (it.current()) {
00593         QListViewItem *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(QCloseEvent *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       QTimer::singleShot(0, this, SLOT(slotClose()));
00627     } else {
00628       KDialogBase::closeEvent(e);
00629     }
00630 }
00631 
00632 //-----------------------------------------------------------------------------
00633 void SearchWindow::scheduleRename( const QString &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         QString 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(), SIGNAL(finished(bool)),
00680                     this, SLOT(searchDone()));
00681         mTimer->start(200);
00682         enableGUI();
00683     }
00684 }
00685 
00686 //-----------------------------------------------------------------------------
00687 KMMessage *SearchWindow::indexToMessage( QListViewItem *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( QListViewItem *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( QListViewItem *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(QListViewItem *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 (QListViewItemIterator 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     QListViewItem *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::decryptedCopySelectedToFolder( int menuId )
00814 {
00815     KMFolder *dest = mMenuToFolder[menuId];
00816     if (!dest)
00817         return;
00818 
00819     KMMessageList msgList = selectedMessages();
00820     KMCommand *command = new KMCopyCommand( dest, msgList, true );
00821     command->start();
00822 }
00823 
00824 
00825 //-----------------------------------------------------------------------------
00826 void SearchWindow::updateContextMenuActions()
00827 {
00828     int count = selectedMessages().count();
00829     bool single_actions = count == 1;
00830     mReplyAction->setEnabled( single_actions );
00831     mReplyAllAction->setEnabled( single_actions );
00832     mReplyListAction->setEnabled( single_actions );
00833     mPrintAction->setEnabled( single_actions );
00834     mForwardDigestAction->setEnabled( !single_actions );
00835     mRedirectAction->setEnabled( single_actions );
00836     mCopyAction->setEnabled( count > 0 );
00837     mCutAction->setEnabled( count > 0 );
00838 }
00839 
00840 //-----------------------------------------------------------------------------
00841 void SearchWindow::slotContextMenuRequested( QListViewItem *lvi, const QPoint &, int )
00842 {
00843     if (!lvi)
00844         return;
00845     mLbxMatches->setSelected( lvi, true );
00846     mLbxMatches->setCurrentItem( lvi );
00847     // FIXME is this ever unGetMsg()'d?
00848     if (!message())
00849         return;
00850     QPopupMenu *menu = new QPopupMenu(this);
00851     updateContextMenuActions();
00852 
00853     mMenuToFolder.clear();
00854     QPopupMenu *msgMoveMenu = new QPopupMenu(menu);
00855     mKMMainWidget->folderTree()->folderToPopupMenu( KMFolderTree::MoveMessage,
00856         this, &mMenuToFolder, msgMoveMenu );
00857     QPopupMenu *msgCopyMenu = new QPopupMenu(menu);
00858     mKMMainWidget->folderTree()->folderToPopupMenu( KMFolderTree::CopyMessage,
00859         this, &mMenuToFolder, msgCopyMenu );
00860     QPopupMenu *msgDecCopyMenu = new QPopupMenu(menu);
00861     mKMMainWidget->folderTree()->folderToPopupMenu( KMFolderTree::DecCopyMessage,
00862         this, &mMenuToFolder, msgDecCopyMenu );
00863 
00864     // show most used actions
00865     mReplyAction->plug(menu);
00866     mReplyAllAction->plug(menu);
00867     mReplyListAction->plug(menu);
00868     mForwardActionMenu->plug(menu);
00869     menu->insertSeparator();
00870     mCopyAction->plug(menu);
00871     mCutAction->plug(menu);
00872     menu->insertItem(i18n("&Copy To"), msgCopyMenu);
00873     menu->insertItem(i18n("&Decrypted Copy To"), msgDecCopyMenu);
00874     menu->insertItem(i18n("&Move To"), msgMoveMenu);
00875     menu->insertSeparator();
00876     mSaveAsAction->plug(menu);
00877     mSaveAtchAction->plug(menu);
00878     mPrintAction->plug(menu);
00879     menu->insertSeparator();
00880     mClearAction->plug(menu);
00881     menu->exec (QCursor::pos(), 0);
00882     delete menu;
00883 }
00884 
00885 //-----------------------------------------------------------------------------
00886 void SearchWindow::slotClearSelection()
00887 {
00888     mLbxMatches->clearSelection();
00889 }
00890 
00891 //-----------------------------------------------------------------------------
00892 void SearchWindow::slotReplyToMsg()
00893 {
00894     KMCommand *command = new KMReplyToCommand(this, message());
00895     command->start();
00896 }
00897 
00898 //-----------------------------------------------------------------------------
00899 void SearchWindow::slotReplyAllToMsg()
00900 {
00901     KMCommand *command = new KMReplyToAllCommand(this, message());
00902     command->start();
00903 }
00904 
00905 //-----------------------------------------------------------------------------
00906 void SearchWindow::slotReplyListToMsg()
00907 {
00908     KMCommand *command = new KMReplyListCommand(this, message());
00909     command->start();
00910 }
00911 
00912 //-----------------------------------------------------------------------------
00913 void SearchWindow::slotForwardInlineMsg()
00914 {
00915     KMCommand *command = new KMForwardInlineCommand(this, selectedMessages());
00916     command->start();
00917 }
00918 
00919 //-----------------------------------------------------------------------------
00920 void SearchWindow::slotForwardAttachedMsg()
00921 {
00922     KMCommand *command = new KMForwardAttachedCommand(this, selectedMessages());
00923     command->start();
00924 }
00925 
00926 //-----------------------------------------------------------------------------
00927 void SearchWindow::slotForwardDigestMsg()
00928 {
00929     KMCommand *command = new KMForwardDigestCommand(this, selectedMessages());
00930     command->start();
00931 }
00932 
00933 //-----------------------------------------------------------------------------
00934 void SearchWindow::slotRedirectMsg()
00935 {
00936     KMCommand *command = new KMRedirectCommand(this, message());
00937     command->start();
00938 }
00939 
00940 //-----------------------------------------------------------------------------
00941 void SearchWindow::slotSaveMsg()
00942 {
00943     KMSaveMsgCommand *saveCommand = new KMSaveMsgCommand(this,
00944                                                          selectedMessages());
00945     if (saveCommand->url().isEmpty())
00946         delete saveCommand;
00947     else
00948         saveCommand->start();
00949 }
00950 //-----------------------------------------------------------------------------
00951 void SearchWindow::slotSaveAttachments()
00952 {
00953     KMSaveAttachmentsCommand *saveCommand = new KMSaveAttachmentsCommand(this,
00954                                                                          selectedMessages());
00955     saveCommand->start();
00956 }
00957 
00958 
00959 //-----------------------------------------------------------------------------
00960 void SearchWindow::slotPrintMsg()
00961 {
00962     KMCommand *command = new KMPrintCommand(this, message());
00963     command->start();
00964 }
00965 
00966 void SearchWindow::slotCopyMsgs()
00967 {
00968   QValueList<Q_UINT32> list = MessageCopyHelper::serNumListFromMsgList( selectedMessages() );
00969   mKMMainWidget->headers()->setCopiedMessages( list, false );
00970 }
00971 
00972 void SearchWindow::slotCutMsgs()
00973 {
00974   QValueList<Q_UINT32> list = MessageCopyHelper::serNumListFromMsgList( selectedMessages() );
00975   mKMMainWidget->headers()->setCopiedMessages( list, true );
00976 }
00977 
00978 
00979 void SearchWindow::setSearchPattern( const KMSearchPattern& pattern )
00980 {
00981     *mSearchPattern = pattern;
00982     mPatternEdit->setSearchPattern( mSearchPattern );
00983 }
00984 
00985 } // namespace KMail
00986 #include "searchwindow.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys