kmail

searchwindow.cpp
1/*
2 * kmail: KDE mail client
3 * Copyright (c) 1996-1998 Stefan Taferner <taferner@kde.org>
4 * Copyright (c) 2001 Aaron J. Seigo <aseigo@kde.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 */
21#include <config.h>
22#include "kmcommands.h"
23#include "searchwindow.h"
24#include "kmmainwidget.h"
25#include "kmmsgdict.h"
26#include "kmmsgpart.h"
27#include "kmfolderimap.h"
28#include "kmfoldermgr.h"
29#include "kmfoldersearch.h"
30#include "kmfoldertree.h"
31#include "kmheaders.h"
32#include "kmsearchpatternedit.h"
33#include "kmsearchpattern.h"
34#include "folderrequester.h"
35#include "messagecopyhelper.h"
36#include "textsource.h"
37
38#include <tdeapplication.h>
39#include <kdebug.h>
40#include <kstatusbar.h>
41#include <twin.h>
42#include <tdeconfig.h>
43#include <kstdaction.h>
44#include <kiconloader.h>
45
46#include <tqcheckbox.h>
47#include <tqlayout.h>
48#include <klineedit.h>
49#include <tqpushbutton.h>
50#include <tqradiobutton.h>
51#include <tqbuttongroup.h>
52#include <tqcombobox.h>
53#include <tqobjectlist.h> //for mPatternEdit->queryList( 0, "mRuleField" )->first();
54#include <tqcursor.h>
55#include <tqpopupmenu.h>
56
57#include <maillistdrag.h>
58using namespace KPIM;
59
60#include <mimelib/enum.h>
61#include <mimelib/boyermor.h>
62
63#include <assert.h>
64#include <stdlib.h>
65
66namespace KMail {
67
68const int SearchWindow::MSGID_COLUMN = 4;
69
70// TDEListView sub-class for dnd support
71class MatchListView : public TDEListView
72{
73 public:
74 MatchListView( TQWidget *parent, SearchWindow* sw, const char* name = 0 ) :
75 TDEListView( parent, name ),
76 mSearchWindow( sw )
77 {}
78
79 protected:
80 virtual TQDragObject* dragObject()
81 {
82 KMMessageList list = mSearchWindow->selectedMessages();
83 MailList mailList;
84 for ( KMMsgBase* msg = list.first(); msg; msg = list.next() ) {
85 if ( !msg )
86 continue;
87 MailSummary mailSummary( msg->getMsgSerNum(), msg->msgIdMD5(),
88 msg->subject(), msg->fromStrip(),
89 msg->toStrip(), msg->date() );
90 mailList.append( mailSummary );
91 }
92 MailListDrag *d = new MailListDrag( mailList, viewport(), new KMTextSource );
93
94 TQPixmap pixmap;
95 if( mailList.count() == 1 )
96 pixmap = TQPixmap( DesktopIcon("message", TDEIcon::SizeSmall) );
97 else
98 pixmap = TQPixmap( DesktopIcon("application-vnd.tde.tdemultiple", TDEIcon::SizeSmall) );
99
100 d->setPixmap( pixmap );
101 return d;
102 }
103
104 private:
105 SearchWindow* mSearchWindow;
106};
107
108//-----------------------------------------------------------------------------
109SearchWindow::SearchWindow(KMMainWidget* w, const char* name,
110 KMFolder *curFolder, bool modal):
111 KDialogBase(0, name, modal, i18n("Find Messages"),
112 User1 | User2 | Close, User1, false,
113 KGuiItem( i18n("&Search"), "edit-find" ),
114 KStdGuiItem::stop()),
115 mStopped(false),
116 mCloseRequested(false),
117 mSortColumn(0),
118 mSortOrder(Ascending),
119 mFolder(0),
120 mTimer(new TQTimer(this, "mTimer")),
121 mLastFocus(0),
122 mKMMainWidget(w)
123{
124#if !KDE_IS_VERSION( 3, 2, 91 )
125 // HACK - KWin keeps all dialogs on top of their mainwindows, but that's probably
126 // wrong (#76026), and should be done only for modals. CVS HEAD should get
127 // proper fix in KWin (l.lunak@kde.org)
128 XDeleteProperty( tqt_xdisplay(), winId(), XA_WM_TRANSIENT_FOR );
129#endif
130 KWin::setIcons(winId(), tdeApp->icon(), tdeApp->miniIcon());
131
132 TDEConfig* config = KMKernel::config();
133 config->setGroup("SearchDialog");
134
135 TQWidget* searchWidget = new TQWidget(this);
136 TQVBoxLayout *vbl = new TQVBoxLayout( searchWidget, 0, spacingHint(), "kmfs_vbl" );
137
138 TQButtonGroup * radioGroup = new TQButtonGroup( searchWidget );
139 radioGroup->hide();
140
141 mChkbxAllFolders = new TQRadioButton(i18n("Search in &all local folders"), searchWidget);
142 vbl->addWidget( mChkbxAllFolders );
143 radioGroup->insert( mChkbxAllFolders );
144
145 TQHBoxLayout *hbl = new TQHBoxLayout( vbl, spacingHint(), "kmfs_hbl" );
146 mChkbxSpecificFolders = new TQRadioButton(i18n("Search &only in:"), searchWidget);
147 hbl->addWidget(mChkbxSpecificFolders);
148 mChkbxSpecificFolders->setChecked(true);
149 radioGroup->insert( mChkbxSpecificFolders );
150
151 mCbxFolders = new FolderRequester( searchWidget,
152 kmkernel->getKMMainWidget()->folderTree() );
153 mCbxFolders->setMustBeReadWrite( false );
154 mCbxFolders->setFolder(curFolder);
155 hbl->addWidget(mCbxFolders);
156
157 mChkSubFolders = new TQCheckBox(i18n("I&nclude sub-folders"), searchWidget);
158 mChkSubFolders->setChecked(true);
159 hbl->addWidget(mChkSubFolders);
160
161 TQWidget *spacer = new TQWidget( searchWidget, "spacer" );
162 spacer->setMinimumHeight( 2 );
163 vbl->addWidget( spacer );
164
165 mPatternEdit = new KMSearchPatternEdit( "", searchWidget , "spe", false, true );
166 mPatternEdit->setFrameStyle( TQFrame::NoFrame | TQFrame::Plain );
167 mPatternEdit->setInsideMargin( 0 );
168 mSearchPattern = new KMSearchPattern();
169 KMFolderSearch *searchFolder = 0;
170 if (curFolder)
171 searchFolder = dynamic_cast<KMFolderSearch*>(curFolder->storage());
172 if (searchFolder) {
173 TDEConfig config(curFolder->location());
174 KMFolder *root = searchFolder->search()->root();
175 config.setGroup("Search Folder");
176 mSearchPattern->readConfig(&config);
177 if (root) {
178 mChkbxSpecificFolders->setChecked(true);
179 mCbxFolders->setFolder(root);
180 mChkSubFolders->setChecked(searchFolder->search()->recursive());
181 } else {
182 mChkbxAllFolders->setChecked(true);
183 }
184 }
185 mPatternEdit->setSearchPattern( mSearchPattern );
186 TQObjectList *list = mPatternEdit->queryList( 0, "mRuleField" );
187 TQObject *object = 0;
188 if ( list )
189 object = list->first();
190 delete list;
191 if (!searchFolder && object && ::tqt_cast<TQComboBox*>(object))
192 static_cast<TQComboBox*>(object)->setCurrentText(i18n("Subject"));
193
194 vbl->addWidget( mPatternEdit );
195
196 // enable/disable widgets depending on radio buttons:
197 connect( mChkbxSpecificFolders, TQ_SIGNAL(toggled(bool)),
198 mCbxFolders, TQ_SLOT(setEnabled(bool)) );
199 connect( mChkbxSpecificFolders, TQ_SIGNAL(toggled(bool)),
200 mChkSubFolders, TQ_SLOT(setEnabled(bool)) );
201 connect( mChkbxAllFolders, TQ_SIGNAL(toggled(bool)),
202 this, TQ_SLOT(setEnabledSearchButton(bool)) );
203
204 mLbxMatches = new MatchListView(searchWidget, this, "Find Messages");
205
206 /*
207 Default is to sort by date. TODO: Unfortunately this sorts *while*
208 inserting, which looks rather strange - the user cannot read
209 the results so far as they are constantly re-sorted --dnaber
210
211 Sorting is now disabled when a search is started and reenabled
212 when it stops. Items are appended to the list. This not only
213 solves the above problem, but speeds searches with many hits
214 up considerably. - till
215
216 TODO: subclass TDEListViewItem and do proper (and performant)
217 comapare functions
218 */
219 mLbxMatches->setSorting(2, false);
220 mLbxMatches->setShowSortIndicator(true);
221 mLbxMatches->setAllColumnsShowFocus(true);
222 mLbxMatches->setSelectionModeExt(TDEListView::Extended);
223 mLbxMatches->addColumn(i18n("Subject"),
224 config->readNumEntry("SubjectWidth", 150));
225 mLbxMatches->addColumn(i18n("Sender/Receiver"),
226 config->readNumEntry("SenderWidth", 120));
227 mLbxMatches->addColumn(i18n("Date"),
228 config->readNumEntry("DateWidth", 120));
229 mLbxMatches->addColumn(i18n("Folder"),
230 config->readNumEntry("FolderWidth", 100));
231
232 mLbxMatches->addColumn(""); // should be hidden
233 mLbxMatches->setColumnWidthMode( MSGID_COLUMN, TQListView::Manual );
234 mLbxMatches->setColumnWidth(MSGID_COLUMN, 0);
235 mLbxMatches->header()->setResizeEnabled(false, MSGID_COLUMN);
236
237 mLbxMatches->setDragEnabled( true );
238
239 connect( mLbxMatches, TQ_SIGNAL(clicked(TQListViewItem *)),
240 this, TQ_SLOT(slotShowMsg(TQListViewItem *)) );
241 connect( mLbxMatches, TQ_SIGNAL(doubleClicked(TQListViewItem *)),
242 this, TQ_SLOT(slotViewMsg(TQListViewItem *)) );
243 connect( mLbxMatches, TQ_SIGNAL(currentChanged(TQListViewItem *)),
244 this, TQ_SLOT(slotCurrentChanged(TQListViewItem *)) );
245 connect( mLbxMatches, TQ_SIGNAL(contextMenuRequested(TQListViewItem *,const TQPoint &,int)),
246 this, TQ_SLOT(slotContextMenuRequested(TQListViewItem *,const TQPoint &,int)) );
247 vbl->addWidget( mLbxMatches );
248
249 TQHBoxLayout *hbl2 = new TQHBoxLayout( vbl, spacingHint(), "kmfs_hbl2" );
250 mSearchFolderLbl = new TQLabel(i18n("Search folder &name:"), searchWidget);
251 hbl2->addWidget(mSearchFolderLbl);
252 mSearchFolderEdt = new KLineEdit(searchWidget);
253 if (searchFolder)
254 mSearchFolderEdt->setText(searchFolder->folder()->name());
255 else
256 mSearchFolderEdt->setText(i18n("Last Search"));
257
258 mSearchFolderLbl->setBuddy(mSearchFolderEdt);
259 hbl2->addWidget(mSearchFolderEdt);
260 mSearchFolderOpenBtn = new TQPushButton(i18n("Op&en Search Folder"), searchWidget);
261 mSearchFolderOpenBtn->setEnabled(false);
262 hbl2->addWidget(mSearchFolderOpenBtn);
263 connect( mSearchFolderEdt, TQ_SIGNAL( textChanged( const TQString &)),
264 this, TQ_SLOT( scheduleRename( const TQString & )));
265 connect( &mRenameTimer, TQ_SIGNAL( timeout() ),
266 this, TQ_SLOT( renameSearchFolder() ));
267 connect( mSearchFolderOpenBtn, TQ_SIGNAL( clicked() ),
268 this, TQ_SLOT( openSearchFolder() ));
269 mSearchResultOpenBtn = new TQPushButton(i18n("Open &Message"), searchWidget);
270 mSearchResultOpenBtn->setEnabled(false);
271 hbl2->addWidget(mSearchResultOpenBtn);
272 connect( mSearchResultOpenBtn, TQ_SIGNAL( clicked() ),
273 this, TQ_SLOT( slotViewSelectedMsg() ));
274 mStatusBar = new KStatusBar(searchWidget);
275 mStatusBar->insertFixedItem(i18n("AMiddleLengthText..."), 0, true);
276 mStatusBar->changeItem(i18n("Ready."), 0);
277 mStatusBar->setItemAlignment(0, AlignLeft | AlignVCenter);
278 mStatusBar->insertItem(TQString(), 1, 1, true);
279 mStatusBar->setItemAlignment(1, AlignLeft | AlignVCenter);
280 vbl->addWidget(mStatusBar);
281
282 int mainWidth = config->readNumEntry("SearchWidgetWidth", 0);
283 int mainHeight = config->readNumEntry("SearchWidgetHeight", 0);
284
285 if (mainWidth || mainHeight)
286 resize(mainWidth, mainHeight);
287
288 setMainWidget(searchWidget);
289 setButtonBoxOrientation(TQt::Vertical);
290
291 mBtnSearch = actionButton(KDialogBase::User1);
292 mBtnStop = actionButton(KDialogBase::User2);
293 mBtnStop->setEnabled(false);
294
295 connect(this, TQ_SIGNAL(user1Clicked()), TQ_SLOT(slotSearch()));
296 connect(this, TQ_SIGNAL(user2Clicked()), TQ_SLOT(slotStop()));
297 connect(this, TQ_SIGNAL(finished()), this, TQ_SLOT(deleteLater()));
298
299 // give focus to the value field of the first search rule
300 object = mPatternEdit->child( "regExpLineEdit" );
301 if ( object && object->isWidgetType() ) {
302 static_cast<TQWidget*>(object)->setFocus();
303 //kdDebug(5006) << "SearchWindow: focus has been given to widget "
304 // << object->name() << endl;
305 }
306 else
307 kdDebug(5006) << "SearchWindow: regExpLineEdit not found" << endl;
308
309 //set up actions
310 TDEActionCollection *ac = actionCollection();
311 ac->setWidget( this );
312 mReplyAction = new TDEAction( i18n("&Reply..."), "mail-reply-sender", 0, this,
313 TQ_SLOT(slotReplyToMsg()), ac, "search_reply" );
314 mReplyAllAction = new TDEAction( i18n("Reply to &All..."), "mail-reply-all",
315 0, this, TQ_SLOT(slotReplyAllToMsg()),
316 ac, "search_reply_all" );
317 mReplyListAction = new TDEAction( i18n("Reply to Mailing-&List..."),
318 "mail_replylist", 0, this,
319 TQ_SLOT(slotReplyListToMsg()), ac,
320 "search_reply_list" );
321 mForwardActionMenu = new TDEActionMenu( i18n("Message->","&Forward"),
322 "mail-forward", ac,
323 "search_message_forward" );
324 connect( mForwardActionMenu, TQ_SIGNAL(activated()), this,
325 TQ_SLOT(slotForwardInlineMsg()) );
326 mForwardAttachedAction = new TDEAction( i18n("Message->Forward->","As &Attachment..."),
327 "mail-forward", 0, this,
328 TQ_SLOT(slotForwardAttachedMsg()), ac,
329 "search_message_forward_as_attachment" );
330 mForwardInlineAction = new TDEAction( i18n("&Inline..."),
331 "mail-forward", 0, this,
332 TQ_SLOT(slotForwardInlineMsg()), ac,
333 "search_message_forward_inline" );
334 if ( GlobalSettings::self()->forwardingInlineByDefault() ) {
335 mForwardActionMenu->insert( mForwardInlineAction );
336 mForwardActionMenu->insert( mForwardAttachedAction );
337 } else {
338 mForwardActionMenu->insert( mForwardAttachedAction );
339 mForwardActionMenu->insert( mForwardInlineAction );
340 }
341
342 mForwardDigestAction = new TDEAction( i18n("Message->Forward->","As Di&gest..."),
343 "mail-forward", 0, this,
344 TQ_SLOT(slotForwardDigestMsg()), ac,
345 "search_message_forward_as_digest" );
346 mForwardActionMenu->insert( mForwardDigestAction );
347 mRedirectAction = new TDEAction( i18n("Message->Forward->","&Redirect..."),
348 "mail-forward", 0, this,
349 TQ_SLOT(slotRedirectMsg()), ac,
350 "search_message_forward_redirect" );
351 mForwardActionMenu->insert( mRedirectAction );
352 mSaveAsAction = KStdAction::saveAs( this, TQ_SLOT(slotSaveMsg()), ac, "search_file_save_as" );
353 mSaveAtchAction = new TDEAction( i18n("Save Attachments..."), "attach", 0,
354 this, TQ_SLOT(slotSaveAttachments()), ac, "search_save_attachments" );
355
356 mPrintAction = KStdAction::print( this, TQ_SLOT(slotPrintMsg()), ac, "search_print" );
357 mClearAction = new TDEAction( i18n("Clear Selection"), 0, 0, this,
358 TQ_SLOT(slotClearSelection()), ac, "search_clear_selection" );
359
360 mCopyAction = KStdAction::copy( this, TQ_SLOT(slotCopyMsgs()), ac, "search_copy_messages" );
361 mCutAction = KStdAction::cut( this, TQ_SLOT(slotCutMsgs()), ac, "search_cut_messages" );
362
363 connect(mTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(updStatus()));
364 connect(kmkernel->searchFolderMgr(), TQ_SIGNAL(folderInvalidated(KMFolder*)),
365 this, TQ_SLOT(folderInvalidated(KMFolder*)));
366
367 connect(mCbxFolders, TQ_SIGNAL(folderChanged(KMFolder*)),
368 this, TQ_SLOT(slotFolderActivated()));
369
370}
371
372//-----------------------------------------------------------------------------
373SearchWindow::~SearchWindow()
374{
375 TQValueListIterator<TQGuardedPtr<KMFolder> > fit;
376 for ( fit = mFolders.begin(); fit != mFolders.end(); ++fit ) {
377 if (!(*fit))
378 continue;
379 (*fit)->close("searchwindow");
380 }
381
382 TDEConfig* config = KMKernel::config();
383 config->setGroup("SearchDialog");
384 config->writeEntry("SubjectWidth", mLbxMatches->columnWidth(0));
385 config->writeEntry("SenderWidth", mLbxMatches->columnWidth(1));
386 config->writeEntry("DateWidth", mLbxMatches->columnWidth(2));
387 config->writeEntry("FolderWidth", mLbxMatches->columnWidth(3));
388 config->writeEntry("SearchWidgetWidth", width());
389 config->writeEntry("SearchWidgetHeight", height());
390 config->sync();
391}
392
393void SearchWindow::setEnabledSearchButton(bool)
394{
395 //Make sure that button is enable
396 //Before when we selected a folder == "Local Folder" as that it was not a folder
397 //search button was disable, and when we select "Search in all local folder"
398 //Search button was never enabled :(
399 mBtnSearch->setEnabled( true );
400}
401
402//-----------------------------------------------------------------------------
404{
405 TQString genMsg, detailMsg, procMsg;
406 int numMatches = 0, numProcessed = 0;
407 KMSearch const *search = (mFolder) ? (mFolder->search()) : 0;
408 TQString folderName;
409 if (search) {
410 numMatches = search->foundCount();
411 numProcessed = search->searchCount();
412 folderName = search->currentFolder();
413 }
414
415 if (search && !search->running()) {
416 procMsg = i18n("%n message searched", "%n messages searched",
417 numProcessed);
418 if(!mStopped) {
419 genMsg = i18n("Done.");
420 detailMsg = i18n("%n match in %1", "%n matches in %1",
421 numMatches).arg(procMsg);
422 } else {
423 genMsg = i18n("Search canceled.");
424 detailMsg = i18n("%n match so far in %1", "%n matches so far in %1",
425 numMatches).arg(procMsg);
426 }
427 } else {
428 procMsg = i18n("%n message", "%n messages", numProcessed);
429 genMsg = i18n("%n match", "%n matches", numMatches);
430 detailMsg = i18n("Searching in %1. %2 searched so far")
431 .arg(folderName).arg(procMsg);
432 }
433
434 mStatusBar->changeItem(genMsg, 0);
435 mStatusBar->changeItem(detailMsg, 1);
436}
437
438
439//-----------------------------------------------------------------------------
440void SearchWindow::keyPressEvent(TQKeyEvent *evt)
441{
442 KMSearch const *search = (mFolder) ? mFolder->search() : 0;
443 bool searching = (search) ? search->running() : false;
444 if (evt->key() == Key_Escape && searching) {
445 mFolder->stopSearch();
446 return;
447 }
448
449 KDialogBase::keyPressEvent(evt);
450}
451
452
453//-----------------------------------------------------------------------------
454void SearchWindow::slotFolderActivated()
455{
456 mChkbxSpecificFolders->setChecked(true);
457}
458
459//-----------------------------------------------------------------------------
461{
462 mChkbxSpecificFolders->setChecked(true);
463 mCbxFolders->setFolder(curFolder);
464}
465
466//-----------------------------------------------------------------------------
467void SearchWindow::slotSearch()
468{
469 mLastFocus = focusWidget();
470 mBtnSearch->setFocus(); // set focus so we don't miss key event
471
472 mStopped = false;
473 mFetchingInProgress = 0;
474
475 mSearchFolderOpenBtn->setEnabled(true);
476 if ( mSearchFolderEdt->text().isEmpty() ) {
477 mSearchFolderEdt->setText( i18n("Last Search") );
478 }
479 mBtnSearch->setEnabled(false);
480 mBtnStop->setEnabled(true);
481
482 mLbxMatches->clear();
483
484 mSortColumn = mLbxMatches->sortColumn();
485 mSortOrder = mLbxMatches->sortOrder();
486 mLbxMatches->setSorting(-1);
487 mLbxMatches->setShowSortIndicator(false);
488
489 // If we haven't openend an existing search folder, find or
490 // create one.
491 if (!mFolder) {
492 KMFolderMgr *mgr = kmkernel->searchFolderMgr();
493 TQString baseName = mSearchFolderEdt->text();
494 TQString fullName = baseName;
495 int count = 0;
496 KMFolder *folder;
497 while ((folder = mgr->find(fullName))) {
498 if (folder->storage()->inherits("KMFolderSearch"))
499 break;
500 fullName = TQString("%1 %2").arg(baseName).arg(++count);
501 }
502
503 if (!folder)
504 folder = mgr->createFolder(fullName, false, KMFolderTypeSearch,
505 &mgr->dir());
506
507 mFolder = dynamic_cast<KMFolderSearch*>( folder->storage() );
508 }
509 mFolder->stopSearch();
510 disconnect(mFolder, TQ_SIGNAL(msgAdded(int)),
511 this, TQ_SLOT(slotAddMsg(int)));
512 disconnect(mFolder, TQ_SIGNAL(msgRemoved(KMFolder*, TQ_UINT32)),
513 this, TQ_SLOT(slotRemoveMsg(KMFolder*, TQ_UINT32)));
514 connect(mFolder, TQ_SIGNAL(msgAdded(int)),
515 this, TQ_SLOT(slotAddMsg(int)));
516 connect(mFolder, TQ_SIGNAL(msgRemoved(KMFolder*, TQ_UINT32)),
517 this, TQ_SLOT(slotRemoveMsg(KMFolder*, TQ_UINT32)));
518 mSearchFolderEdt->setEnabled(false);
519 KMSearch *search = new KMSearch();
520 connect(search, TQ_SIGNAL(finished(bool)),
521 this, TQ_SLOT(searchDone()));
522 if (mChkbxAllFolders->isChecked()) {
523 search->setRecursive(true);
524 } else {
525 search->setRoot(mCbxFolders->folder());
526 search->setRecursive(mChkSubFolders->isChecked());
527 }
528
529 mPatternEdit->updateSearchPattern();
530 KMSearchPattern *searchPattern = new KMSearchPattern();
531 *searchPattern = *mSearchPattern; //deep copy
532 searchPattern->purify();
533 search->setSearchPattern(searchPattern);
534 mFolder->setSearch(search);
535 enableGUI();
536
537 mTimer->start(200);
538}
539
540//-----------------------------------------------------------------------------
542{
543 mTimer->stop();
544 updStatus();
545
546 TQTimer::singleShot(0, this, TQ_SLOT(enableGUI()));
547 if(mLastFocus)
548 mLastFocus->setFocus();
549 if (mCloseRequested)
550 close();
551
552 mLbxMatches->setSorting(mSortColumn, mSortOrder == Ascending);
553 mLbxMatches->setShowSortIndicator(true);
554
555 mSearchFolderEdt->setEnabled(true);
556}
557
558void SearchWindow::slotAddMsg(int idx)
559{
560 if (!mFolder)
561 return;
562 bool unget = !mFolder->isMessage(idx);
563 KMMessage *msg = mFolder->getMsg(idx);
564 TQString from, fName;
565 KMFolder *pFolder = msg->parent();
566 if (!mFolders.contains(pFolder)) {
567 mFolders.append(pFolder);
568 pFolder->open("searchwindow");
569 }
570 if(pFolder->whoField() == "To")
571 from = msg->to();
572 else
573 from = msg->from();
574 if (pFolder->isSystemFolder())
575 fName = i18n(pFolder->name().utf8());
576 else
577 fName = pFolder->name();
578
579 (void)new TDEListViewItem(mLbxMatches, mLbxMatches->lastItem(),
580 msg->subject(), from, msg->dateIsoStr(),
581 fName,
582 TQString::number(mFolder->serNum(idx)));
583 if (unget)
584 mFolder->unGetMsg(idx);
585}
586
587void SearchWindow::slotRemoveMsg(KMFolder *, TQ_UINT32 serNum)
588{
589 if (!mFolder)
590 return;
591 TQListViewItemIterator it(mLbxMatches);
592 while (it.current()) {
593 TQListViewItem *item = *it;
594 if (serNum == (*it)->text(MSGID_COLUMN).toUInt()) {
595 delete item;
596 return;
597 }
598 ++it;
599 }
600}
601
602//-----------------------------------------------------------------------------
603void SearchWindow::slotStop()
604{
605 if (mFolder)
606 mFolder->stopSearch();
607 mStopped = true;
608 mBtnStop->setEnabled(false);
609}
610
611//-----------------------------------------------------------------------------
612void SearchWindow::slotClose()
613{
614 accept();
615}
616
617
618//-----------------------------------------------------------------------------
619void SearchWindow::closeEvent(TQCloseEvent *e)
620{
621 if (mFolder && mFolder->search() && mFolder->search()->running()) {
622 mCloseRequested = true;
623 //Cancel search in progress by setting the search folder search to
624 //the null search
625 mFolder->setSearch(new KMSearch());
626 TQTimer::singleShot(0, this, TQ_SLOT(slotClose()));
627 } else {
628 KDialogBase::closeEvent(e);
629 }
630}
631
632//-----------------------------------------------------------------------------
633void SearchWindow::scheduleRename( const TQString &s)
634{
635 if (!s.isEmpty() ) {
636 mRenameTimer.start(250, true);
637 mSearchFolderOpenBtn->setEnabled(false);
638 } else {
639 mRenameTimer.stop();
640 mSearchFolderOpenBtn->setEnabled(!s.isEmpty());
641 }
642}
643
644//-----------------------------------------------------------------------------
645void SearchWindow::renameSearchFolder()
646{
647 if (mFolder && (mFolder->folder()->name() != mSearchFolderEdt->text())) {
648 int i = 1;
649 TQString name = mSearchFolderEdt->text();
650 while (i < 100) {
651 if (!kmkernel->searchFolderMgr()->find( name )) {
652 mFolder->rename( name );
653 kmkernel->searchFolderMgr()->contentsChanged();
654 break;
655 }
656 name.setNum( i );
657 name = mSearchFolderEdt->text() + " " + name;
658 ++i;
659 }
660 }
661 if ( mFolder )
662 mSearchFolderOpenBtn->setEnabled(true);
663}
664
665void SearchWindow::openSearchFolder()
666{
667 Q_ASSERT( mFolder );
668 renameSearchFolder();
669 mKMMainWidget->slotSelectFolder( mFolder->folder() );
670 slotClose();
671}
672
673//-----------------------------------------------------------------------------
674void SearchWindow::folderInvalidated(KMFolder *folder)
675{
676 if (folder->storage() == mFolder) {
677 mLbxMatches->clear();
678 if (mFolder->search())
679 connect(mFolder->search(), TQ_SIGNAL(finished(bool)),
680 this, TQ_SLOT(searchDone()));
681 mTimer->start(200);
682 enableGUI();
683 }
684}
685
686//-----------------------------------------------------------------------------
687KMMessage *SearchWindow::indexToMessage( TQListViewItem *item )
688{
689 if( !item ) {
690 return 0;
691 }
692
693 KMFolder *folder;
694 int msgIndex;
695 KMMsgDict::instance()->getLocation( item->text( MSGID_COLUMN ).toUInt(),
696 &folder, &msgIndex );
697
698 if ( !folder || msgIndex < 0 ) {
699 return 0;
700 }
701
702 mKMMainWidget->slotSelectFolder( folder );
703 return folder->getMsg( msgIndex );
704}
705
706//-----------------------------------------------------------------------------
707bool SearchWindow::slotShowMsg( TQListViewItem *item )
708{
709 KMMessage *message = indexToMessage( item );
710 if ( message ) {
711 mKMMainWidget->slotSelectMessage( message );
712 return true;
713 }
714 return false;
715}
716
717//-----------------------------------------------------------------------------
718void SearchWindow::slotViewSelectedMsg()
719{
720 slotViewMsg( mLbxMatches->currentItem() );
721}
722
723//-----------------------------------------------------------------------------
724bool SearchWindow::slotViewMsg( TQListViewItem *item )
725{
726 KMMessage *message = indexToMessage( item );
727 if ( message ) {
728 mKMMainWidget->slotMsgActivated( message );
729 return true;
730 }
731 return false;
732}
733
734//-----------------------------------------------------------------------------
735void SearchWindow::slotCurrentChanged(TQListViewItem *item)
736{
737 mSearchResultOpenBtn->setEnabled(item!=0);
738}
739
740//-----------------------------------------------------------------------------
741void SearchWindow::enableGUI()
742{
743 KMSearch const *search = (mFolder) ? (mFolder->search()) : 0;
744 bool searching = (search) ? (search->running()) : false;
745 actionButton(KDialogBase::Close)->setEnabled(!searching);
746 mCbxFolders->setEnabled(!searching && !mChkbxAllFolders->isChecked());
747 mChkSubFolders->setEnabled(!searching && !mChkbxAllFolders->isChecked());
748 mChkbxAllFolders->setEnabled(!searching);
749 mChkbxSpecificFolders->setEnabled(!searching);
750 mPatternEdit->setEnabled(!searching);
751 mBtnSearch->setEnabled(!searching);
752 mBtnStop->setEnabled(searching);
753}
754
755
756//-----------------------------------------------------------------------------
758{
759 KMMessageList msgList;
760 KMFolder* folder = 0;
761 int msgIndex = -1;
762 for (TQListViewItemIterator it(mLbxMatches); it.current(); it++)
763 if (it.current()->isSelected()) {
764 KMMsgDict::instance()->getLocation((*it)->text(MSGID_COLUMN).toUInt(),
765 &folder, &msgIndex);
766 if (folder && msgIndex >= 0)
767 msgList.append(folder->getMsgBase(msgIndex));
768 }
769 return msgList;
770}
771
772//-----------------------------------------------------------------------------
774{
775 TQListViewItem *item = mLbxMatches->currentItem();
776 KMFolder* folder = 0;
777 int msgIndex = -1;
778 if (!item)
779 return 0;
780 KMMsgDict::instance()->getLocation(item->text(MSGID_COLUMN).toUInt(),
781 &folder, &msgIndex);
782 if (!folder || msgIndex < 0)
783 return 0;
784
785 return folder->getMsg(msgIndex);
786}
787
788//-----------------------------------------------------------------------------
789void SearchWindow::moveSelectedToFolder( int menuId )
790{
791 KMFolder *dest = mMenuToFolder[menuId];
792 if (!dest)
793 return;
794
795 KMMessageList msgList = selectedMessages();
796 KMCommand *command = new KMMoveCommand( dest, msgList );
797 command->start();
798}
799
800//-----------------------------------------------------------------------------
801void SearchWindow::copySelectedToFolder( int menuId )
802{
803 KMFolder *dest = mMenuToFolder[menuId];
804 if (!dest)
805 return;
806
807 KMMessageList msgList = selectedMessages();
808 KMCommand *command = new KMCopyCommand( dest, msgList );
809 command->start();
810}
811
812//-----------------------------------------------------------------------------
813void SearchWindow::updateContextMenuActions()
814{
815 int count = selectedMessages().count();
816 bool single_actions = count == 1;
817 mReplyAction->setEnabled( single_actions );
818 mReplyAllAction->setEnabled( single_actions );
819 mReplyListAction->setEnabled( single_actions );
820 mPrintAction->setEnabled( single_actions );
821 mForwardDigestAction->setEnabled( !single_actions );
822 mRedirectAction->setEnabled( single_actions );
823 mCopyAction->setEnabled( count > 0 );
824 mCutAction->setEnabled( count > 0 );
825}
826
827//-----------------------------------------------------------------------------
828void SearchWindow::slotContextMenuRequested( TQListViewItem *lvi, const TQPoint &, int )
829{
830 if (!lvi)
831 return;
832 mLbxMatches->setSelected( lvi, true );
833 mLbxMatches->setCurrentItem( lvi );
834 // FIXME is this ever unGetMsg()'d?
835 if (!message())
836 return;
837 TQPopupMenu *menu = new TQPopupMenu(this);
838 updateContextMenuActions();
839
840 mMenuToFolder.clear();
841 TQPopupMenu *msgMoveMenu = new TQPopupMenu(menu);
842 mKMMainWidget->folderTree()->folderToPopupMenu( KMFolderTree::MoveMessage,
843 this, &mMenuToFolder, msgMoveMenu );
844 TQPopupMenu *msgCopyMenu = new TQPopupMenu(menu);
845 mKMMainWidget->folderTree()->folderToPopupMenu( KMFolderTree::CopyMessage,
846 this, &mMenuToFolder, msgCopyMenu );
847
848 // show most used actions
849 mReplyAction->plug(menu);
850 mReplyAllAction->plug(menu);
851 mReplyListAction->plug(menu);
852 mForwardActionMenu->plug(menu);
853 menu->insertSeparator();
854 mCopyAction->plug(menu);
855 mCutAction->plug(menu);
856 menu->insertItem(i18n("&Copy To"), msgCopyMenu);
857 menu->insertItem(i18n("&Move To"), msgMoveMenu);
858 menu->insertSeparator();
859 mSaveAsAction->plug(menu);
860 mSaveAtchAction->plug(menu);
861 mPrintAction->plug(menu);
862 menu->insertSeparator();
863 mClearAction->plug(menu);
864 menu->exec (TQCursor::pos(), 0);
865 delete menu;
866}
867
868//-----------------------------------------------------------------------------
869void SearchWindow::slotClearSelection()
870{
871 mLbxMatches->clearSelection();
872}
873
874//-----------------------------------------------------------------------------
875void SearchWindow::slotReplyToMsg()
876{
877 KMCommand *command = new KMReplyToCommand(this, message());
878 command->start();
879}
880
881//-----------------------------------------------------------------------------
882void SearchWindow::slotReplyAllToMsg()
883{
884 KMCommand *command = new KMReplyToAllCommand(this, message());
885 command->start();
886}
887
888//-----------------------------------------------------------------------------
889void SearchWindow::slotReplyListToMsg()
890{
891 KMCommand *command = new KMReplyListCommand(this, message());
892 command->start();
893}
894
895//-----------------------------------------------------------------------------
896void SearchWindow::slotForwardInlineMsg()
897{
898 KMCommand *command = new KMForwardInlineCommand(this, selectedMessages());
899 command->start();
900}
901
902//-----------------------------------------------------------------------------
903void SearchWindow::slotForwardAttachedMsg()
904{
905 KMCommand *command = new KMForwardAttachedCommand(this, selectedMessages());
906 command->start();
907}
908
909//-----------------------------------------------------------------------------
910void SearchWindow::slotForwardDigestMsg()
911{
912 KMCommand *command = new KMForwardDigestCommand(this, selectedMessages());
913 command->start();
914}
915
916//-----------------------------------------------------------------------------
917void SearchWindow::slotRedirectMsg()
918{
919 KMCommand *command = new KMRedirectCommand(this, message());
920 command->start();
921}
922
923//-----------------------------------------------------------------------------
924void SearchWindow::slotSaveMsg()
925{
926 KMSaveMsgCommand *saveCommand = new KMSaveMsgCommand(this,
928 if (saveCommand->url().isEmpty())
929 delete saveCommand;
930 else
931 saveCommand->start();
932}
933//-----------------------------------------------------------------------------
934void SearchWindow::slotSaveAttachments()
935{
936 KMSaveAttachmentsCommand *saveCommand = new KMSaveAttachmentsCommand(this,
938 saveCommand->start();
939}
940
941
942//-----------------------------------------------------------------------------
943void SearchWindow::slotPrintMsg()
944{
945 KMCommand *command = new KMPrintCommand(this, message());
946 command->start();
947}
948
949void SearchWindow::slotCopyMsgs()
950{
951 TQValueList<TQ_UINT32> list = MessageCopyHelper::serNumListFromMsgList( selectedMessages() );
952 mKMMainWidget->headers()->setCopiedMessages( list, false );
953}
954
955void SearchWindow::slotCutMsgs()
956{
957 TQValueList<TQ_UINT32> list = MessageCopyHelper::serNumListFromMsgList( selectedMessages() );
958 mKMMainWidget->headers()->setCopiedMessages( list, true );
959}
960
961
962void SearchWindow::setSearchPattern( const KMSearchPattern& pattern )
963{
964 *mSearchPattern = pattern;
965 mPatternEdit->setSearchPattern( mSearchPattern );
966}
967
968} // namespace KMail
969#include "searchwindow.moc"
Mail folder.
Definition: kmfolder.h:69
TQString whoField() const
Get / set the name of the field that is used for the Sender/Receiver column in the headers (From/To)
Definition: kmfolder.h:396
bool isSystemFolder() const
Returns true if the folder is a kmail system folder.
Definition: kmfolder.h:369
KMMessage * getMsg(int idx)
Read message at given index.
Definition: kmfolder.cpp:321
const KMMsgBase * getMsgBase(int idx) const
Provides access to the basic message fields that are also stored in the index.
Definition: kmfolder.cpp:360
int open(const char *owner)
Open folder for access.
Definition: kmfolder.cpp:479
TQString location() const
Returns full path to folder file.
Definition: kmfolder.cpp:243
This is a Mime Message.
Definition: kmmessage.h:68
TQString from() const
Get or set the 'From' header field.
Definition: kmmessage.cpp:2015
TQString to() const
Get or set the 'To' header field.
Definition: kmmessage.cpp:1894
TQString subject() const
Get or set the 'Subject' header field.
Definition: kmmessage.cpp:2049
void getLocation(unsigned long key, KMFolder **retFolder, int *retIndex) const
Returns the folder the message represented by the serial number key is in and the index in that folde...
Definition: kmmsgdict.cpp:319
static const KMMsgDict * instance()
Access the globally unique MessageDict.
Definition: kmmsgdict.cpp:167
This widget is intended to be used in the filter configuration as well as in the message search dialo...
void updateSearchPattern()
Updates the search pattern according to the current widget values.
void setSearchPattern(KMSearchPattern *aPattern)
Set the search pattern.
This class is an abstraction of a search over messages.
void readConfig(const TDEConfig *config)
Reads a search pattern from a TDEConfig.
void purify()
Removes all empty rules from the list.
A widget that contains a KLineEdit which shows the current folder and a button that fires a KMFolderS...
KMFolder * folder(void) const
Returns selected folder.
void setFolder(KMFolder *)
Preset the folder.
void setMustBeReadWrite(bool readwrite)
Set if readonly folders should be disabled Be aware that if you disable this the user can also select...
static TQValueList< TQ_UINT32 > serNumListFromMsgList(TQPtrList< KMMsgBase > list)
Converts a KMMsgsBase* list into a serial number list.
virtual void searchDone()
GUI cleanup after search.
KMMessage * message()
Provides access to the currently selected message.
virtual void updStatus(void)
Update status line widget.
virtual void keyPressEvent(TQKeyEvent *)
Reimplemented to react to Escape.
SearchWindow(KMMainWidget *parent, const char *name=0, KMFolder *curFolder=0, bool modal=false)
Creates a new search window.
virtual void closeEvent(TQCloseEvent *)
Reimplemented to stop searching when the window is closed.
KMMessageList selectedMessages()
Provides access to the list of currently selected message in the listview.
void activateFolder(KMFolder *curFolder)
Changes the base folder for search operations to a different folder.
folderdiaquotatab.h
Definition: aboutdata.cpp:40