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 <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 // KListView sub-class for dnd support 00071 class MatchListView : public KListView 00072 { 00073 public: 00074 MatchListView( TQWidget *parent, SearchWindow* sw, const char* name = 0 ) : 00075 KListView( 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", KIcon::SizeSmall) ); 00097 else 00098 pixmap = TQPixmap( 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 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( 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 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 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 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 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, 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 KActionCollection *ac = actionCollection(); 00311 ac->setWidget( this ); 00312 mReplyAction = new KAction( i18n("&Reply..."), "mail_reply", 0, TQT_TQOBJECT(this), 00313 TQT_SLOT(slotReplyToMsg()), ac, "search_reply" ); 00314 mReplyAllAction = new KAction( i18n("Reply to &All..."), "mail_replyall", 00315 0, TQT_TQOBJECT(this), TQT_SLOT(slotReplyAllToMsg()), 00316 ac, "search_reply_all" ); 00317 mReplyListAction = new KAction( i18n("Reply to Mailing-&List..."), 00318 "mail_replylist", 0, TQT_TQOBJECT(this), 00319 TQT_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, TQT_SIGNAL(activated()), this, 00325 TQT_SLOT(slotForwardInlineMsg()) ); 00326 mForwardAttachedAction = new KAction( 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 KAction( 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 KAction( 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 KAction( 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 KAction( 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 KAction( 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 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 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 KListViewItem(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"