kmail

searchwindow.cpp

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