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>
58 using namespace KPIM;
59 
60 #include <mimelib/enum.h>
61 #include <mimelib/boyermor.h>
62 
63 #include <assert.h>
64 #include <stdlib.h>
65 
66 namespace KMail {
67 
68 const int SearchWindow::MSGID_COLUMN = 4;
69 
70 // TDEListView sub-class for dnd support
71 class 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 //-----------------------------------------------------------------------------
109 SearchWindow::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  KWin::setIcons(winId(), kapp->icon(), kapp->miniIcon());
125 
126  TDEConfig* config = KMKernel::config();
127  config->setGroup("SearchDialog");
128 
129  TQWidget* searchWidget = new TQWidget(this);
130  TQVBoxLayout *vbl = new TQVBoxLayout( searchWidget, 0, spacingHint(), "kmfs_vbl" );
131 
132  TQButtonGroup * radioGroup = new TQButtonGroup( searchWidget );
133  radioGroup->hide();
134 
135  mChkbxAllFolders = new TQRadioButton(i18n("Search in &all local folders"), searchWidget);
136  vbl->addWidget( mChkbxAllFolders );
137  radioGroup->insert( mChkbxAllFolders );
138 
139  TQHBoxLayout *hbl = new TQHBoxLayout( vbl, spacingHint(), "kmfs_hbl" );
140  mChkbxSpecificFolders = new TQRadioButton(i18n("Search &only in:"), searchWidget);
141  hbl->addWidget(mChkbxSpecificFolders);
142  mChkbxSpecificFolders->setChecked(true);
143  radioGroup->insert( mChkbxSpecificFolders );
144 
145  mCbxFolders = new FolderRequester( searchWidget,
146  kmkernel->getKMMainWidget()->folderTree() );
147  mCbxFolders->setMustBeReadWrite( false );
148  mCbxFolders->setFolder(curFolder);
149  hbl->addWidget(mCbxFolders);
150 
151  mChkSubFolders = new TQCheckBox(i18n("I&nclude sub-folders"), searchWidget);
152  mChkSubFolders->setChecked(true);
153  hbl->addWidget(mChkSubFolders);
154 
155  TQWidget *spacer = new TQWidget( searchWidget, "spacer" );
156  spacer->setMinimumHeight( 2 );
157  vbl->addWidget( spacer );
158 
159  mPatternEdit = new KMSearchPatternEdit( "", searchWidget , "spe", false, true );
160  mPatternEdit->setFrameStyle( TQFrame::NoFrame | TQFrame::Plain );
161  mPatternEdit->setInsideMargin( 0 );
162  mSearchPattern = new KMSearchPattern();
163  KMFolderSearch *searchFolder = 0;
164  if (curFolder)
165  searchFolder = dynamic_cast<KMFolderSearch*>(curFolder->storage());
166  if (searchFolder) {
167  TDEConfig config(curFolder->location());
168  KMFolder *root = searchFolder->search()->root();
169  config.setGroup("Search Folder");
170  mSearchPattern->readConfig(&config);
171  if (root) {
172  mChkbxSpecificFolders->setChecked(true);
173  mCbxFolders->setFolder(root);
174  mChkSubFolders->setChecked(searchFolder->search()->recursive());
175  } else {
176  mChkbxAllFolders->setChecked(true);
177  }
178  }
179  mPatternEdit->setSearchPattern( mSearchPattern );
180  TQObjectList *list = mPatternEdit->queryList( 0, "mRuleField" );
181  TQObject *object = 0;
182  if ( list )
183  object = list->first();
184  delete list;
185  if (!searchFolder && object && ::tqt_cast<TQComboBox*>(object))
186  static_cast<TQComboBox*>(object)->setCurrentText(i18n("Subject"));
187 
188  vbl->addWidget( mPatternEdit );
189 
190  // enable/disable widgets depending on radio buttons:
191  connect( mChkbxSpecificFolders, TQ_SIGNAL(toggled(bool)),
192  mCbxFolders, TQ_SLOT(setEnabled(bool)) );
193  connect( mChkbxSpecificFolders, TQ_SIGNAL(toggled(bool)),
194  mChkSubFolders, TQ_SLOT(setEnabled(bool)) );
195  connect( mChkbxAllFolders, TQ_SIGNAL(toggled(bool)),
196  this, TQ_SLOT(setEnabledSearchButton(bool)) );
197 
198  mLbxMatches = new MatchListView(searchWidget, this, "Find Messages");
199 
200  /*
201  Default is to sort by date. TODO: Unfortunately this sorts *while*
202  inserting, which looks rather strange - the user cannot read
203  the results so far as they are constantly re-sorted --dnaber
204 
205  Sorting is now disabled when a search is started and reenabled
206  when it stops. Items are appended to the list. This not only
207  solves the above problem, but speeds searches with many hits
208  up considerably. - till
209 
210  TODO: subclass TDEListViewItem and do proper (and performant)
211  comapare functions
212  */
213  mLbxMatches->setSorting(2, false);
214  mLbxMatches->setShowSortIndicator(true);
215  mLbxMatches->setAllColumnsShowFocus(true);
216  mLbxMatches->setSelectionModeExt(TDEListView::Extended);
217  mLbxMatches->addColumn(i18n("Subject"),
218  config->readNumEntry("SubjectWidth", 150));
219  mLbxMatches->addColumn(i18n("Sender/Receiver"),
220  config->readNumEntry("SenderWidth", 120));
221  mLbxMatches->addColumn(i18n("Date"),
222  config->readNumEntry("DateWidth", 120));
223  mLbxMatches->addColumn(i18n("Folder"),
224  config->readNumEntry("FolderWidth", 100));
225 
226  mLbxMatches->addColumn(""); // should be hidden
227  mLbxMatches->setColumnWidthMode( MSGID_COLUMN, TQListView::Manual );
228  mLbxMatches->setColumnWidth(MSGID_COLUMN, 0);
229  mLbxMatches->header()->setResizeEnabled(false, MSGID_COLUMN);
230 
231  mLbxMatches->setDragEnabled( true );
232 
233  connect( mLbxMatches, TQ_SIGNAL(clicked(TQListViewItem *)),
234  this, TQ_SLOT(slotShowMsg(TQListViewItem *)) );
235  connect( mLbxMatches, TQ_SIGNAL(doubleClicked(TQListViewItem *)),
236  this, TQ_SLOT(slotViewMsg(TQListViewItem *)) );
237  connect( mLbxMatches, TQ_SIGNAL(currentChanged(TQListViewItem *)),
238  this, TQ_SLOT(slotCurrentChanged(TQListViewItem *)) );
239  connect( mLbxMatches, TQ_SIGNAL(contextMenuRequested(TQListViewItem *,const TQPoint &,int)),
240  this, TQ_SLOT(slotContextMenuRequested(TQListViewItem *,const TQPoint &,int)) );
241  vbl->addWidget( mLbxMatches );
242 
243  TQHBoxLayout *hbl2 = new TQHBoxLayout( vbl, spacingHint(), "kmfs_hbl2" );
244  mSearchFolderLbl = new TQLabel(i18n("Search folder &name:"), searchWidget);
245  hbl2->addWidget(mSearchFolderLbl);
246  mSearchFolderEdt = new KLineEdit(searchWidget);
247  if (searchFolder)
248  mSearchFolderEdt->setText(searchFolder->folder()->name());
249  else
250  mSearchFolderEdt->setText(i18n("Last Search"));
251 
252  mSearchFolderLbl->setBuddy(mSearchFolderEdt);
253  hbl2->addWidget(mSearchFolderEdt);
254  mSearchFolderOpenBtn = new TQPushButton(i18n("Op&en Search Folder"), searchWidget);
255  mSearchFolderOpenBtn->setEnabled(false);
256  hbl2->addWidget(mSearchFolderOpenBtn);
257  connect( mSearchFolderEdt, TQ_SIGNAL( textChanged( const TQString &)),
258  this, TQ_SLOT( scheduleRename( const TQString & )));
259  connect( &mRenameTimer, TQ_SIGNAL( timeout() ),
260  this, TQ_SLOT( renameSearchFolder() ));
261  connect( mSearchFolderOpenBtn, TQ_SIGNAL( clicked() ),
262  this, TQ_SLOT( openSearchFolder() ));
263  mSearchResultOpenBtn = new TQPushButton(i18n("Open &Message"), searchWidget);
264  mSearchResultOpenBtn->setEnabled(false);
265  hbl2->addWidget(mSearchResultOpenBtn);
266  connect( mSearchResultOpenBtn, TQ_SIGNAL( clicked() ),
267  this, TQ_SLOT( slotViewSelectedMsg() ));
268  mStatusBar = new KStatusBar(searchWidget);
269  mStatusBar->insertFixedItem(i18n("AMiddleLengthText..."), 0, true);
270  mStatusBar->changeItem(i18n("Ready."), 0);
271  mStatusBar->setItemAlignment(0, AlignLeft | AlignVCenter);
272  mStatusBar->insertItem(TQString(), 1, 1, true);
273  mStatusBar->setItemAlignment(1, AlignLeft | AlignVCenter);
274  vbl->addWidget(mStatusBar);
275 
276  int mainWidth = config->readNumEntry("SearchWidgetWidth", 0);
277  int mainHeight = config->readNumEntry("SearchWidgetHeight", 0);
278 
279  if (mainWidth || mainHeight)
280  resize(mainWidth, mainHeight);
281 
282  setMainWidget(searchWidget);
283  setButtonBoxOrientation(TQt::Vertical);
284 
285  mBtnSearch = actionButton(KDialogBase::User1);
286  mBtnStop = actionButton(KDialogBase::User2);
287  mBtnStop->setEnabled(false);
288 
289  connect(this, TQ_SIGNAL(user1Clicked()), TQ_SLOT(slotSearch()));
290  connect(this, TQ_SIGNAL(user2Clicked()), TQ_SLOT(slotStop()));
291  connect(this, TQ_SIGNAL(finished()), this, TQ_SLOT(deleteLater()));
292 
293  // give focus to the value field of the first search rule
294  object = mPatternEdit->child( "regExpLineEdit" );
295  if ( object && object->isWidgetType() ) {
296  static_cast<TQWidget*>(object)->setFocus();
297  //kdDebug(5006) << "SearchWindow: focus has been given to widget "
298  // << object->name() << endl;
299  }
300  else
301  kdDebug(5006) << "SearchWindow: regExpLineEdit not found" << endl;
302 
303  //set up actions
304  TDEActionCollection *ac = actionCollection();
305  ac->setWidget( this );
306  mReplyAction = new TDEAction( i18n("&Reply..."), "mail-reply-sender", 0, this,
307  TQ_SLOT(slotReplyToMsg()), ac, "search_reply" );
308  mReplyAllAction = new TDEAction( i18n("Reply to &All..."), "mail-reply-all",
309  0, this, TQ_SLOT(slotReplyAllToMsg()),
310  ac, "search_reply_all" );
311  mReplyListAction = new TDEAction( i18n("Reply to Mailing-&List..."),
312  "mail_replylist", 0, this,
313  TQ_SLOT(slotReplyListToMsg()), ac,
314  "search_reply_list" );
315  mForwardActionMenu = new TDEActionMenu( i18n("Message->","&Forward"),
316  "mail-forward", ac,
317  "search_message_forward" );
318  connect( mForwardActionMenu, TQ_SIGNAL(activated()), this,
319  TQ_SLOT(slotForwardInlineMsg()) );
320  mForwardAttachedAction = new TDEAction( i18n("Message->Forward->","As &Attachment..."),
321  "mail-forward", 0, this,
322  TQ_SLOT(slotForwardAttachedMsg()), ac,
323  "search_message_forward_as_attachment" );
324  mForwardInlineAction = new TDEAction( i18n("&Inline..."),
325  "mail-forward", 0, this,
326  TQ_SLOT(slotForwardInlineMsg()), ac,
327  "search_message_forward_inline" );
328  if ( GlobalSettings::self()->forwardingInlineByDefault() ) {
329  mForwardActionMenu->insert( mForwardInlineAction );
330  mForwardActionMenu->insert( mForwardAttachedAction );
331  } else {
332  mForwardActionMenu->insert( mForwardAttachedAction );
333  mForwardActionMenu->insert( mForwardInlineAction );
334  }
335 
336  mForwardDigestAction = new TDEAction( i18n("Message->Forward->","As Di&gest..."),
337  "mail-forward", 0, this,
338  TQ_SLOT(slotForwardDigestMsg()), ac,
339  "search_message_forward_as_digest" );
340  mForwardActionMenu->insert( mForwardDigestAction );
341  mRedirectAction = new TDEAction( i18n("Message->Forward->","&Redirect..."),
342  "mail-forward", 0, this,
343  TQ_SLOT(slotRedirectMsg()), ac,
344  "search_message_forward_redirect" );
345  mForwardActionMenu->insert( mRedirectAction );
346  mSaveAsAction = KStdAction::saveAs( this, TQ_SLOT(slotSaveMsg()), ac, "search_file_save_as" );
347  mSaveAtchAction = new TDEAction( i18n("Save Attachments..."), "attach", 0,
348  this, TQ_SLOT(slotSaveAttachments()), ac, "search_save_attachments" );
349 
350  mPrintAction = KStdAction::print( this, TQ_SLOT(slotPrintMsg()), ac, "search_print" );
351  mClearAction = new TDEAction( i18n("Clear Selection"), 0, 0, this,
352  TQ_SLOT(slotClearSelection()), ac, "search_clear_selection" );
353 
354  mCopyAction = KStdAction::copy( this, TQ_SLOT(slotCopyMsgs()), ac, "search_copy_messages" );
355  mCutAction = KStdAction::cut( this, TQ_SLOT(slotCutMsgs()), ac, "search_cut_messages" );
356 
357  connect(mTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(updStatus()));
358  connect(kmkernel->searchFolderMgr(), TQ_SIGNAL(folderInvalidated(KMFolder*)),
359  this, TQ_SLOT(folderInvalidated(KMFolder*)));
360 
361  connect(mCbxFolders, TQ_SIGNAL(folderChanged(KMFolder*)),
362  this, TQ_SLOT(slotFolderActivated()));
363 
364 }
365 
366 //-----------------------------------------------------------------------------
367 SearchWindow::~SearchWindow()
368 {
369  TQValueListIterator<TQGuardedPtr<KMFolder> > fit;
370  for ( fit = mFolders.begin(); fit != mFolders.end(); ++fit ) {
371  if (!(*fit))
372  continue;
373  (*fit)->close("searchwindow");
374  }
375 
376  TDEConfig* config = KMKernel::config();
377  config->setGroup("SearchDialog");
378  config->writeEntry("SubjectWidth", mLbxMatches->columnWidth(0));
379  config->writeEntry("SenderWidth", mLbxMatches->columnWidth(1));
380  config->writeEntry("DateWidth", mLbxMatches->columnWidth(2));
381  config->writeEntry("FolderWidth", mLbxMatches->columnWidth(3));
382  config->writeEntry("SearchWidgetWidth", width());
383  config->writeEntry("SearchWidgetHeight", height());
384  config->sync();
385 }
386 
387 void SearchWindow::setEnabledSearchButton(bool)
388 {
389  //Make sure that button is enable
390  //Before when we selected a folder == "Local Folder" as that it was not a folder
391  //search button was disable, and when we select "Search in all local folder"
392  //Search button was never enabled :(
393  mBtnSearch->setEnabled( true );
394 }
395 
396 //-----------------------------------------------------------------------------
398 {
399  TQString genMsg, detailMsg, procMsg;
400  int numMatches = 0, numProcessed = 0;
401  KMSearch const *search = (mFolder) ? (mFolder->search()) : 0;
402  TQString folderName;
403  if (search) {
404  numMatches = search->foundCount();
405  numProcessed = search->searchCount();
406  folderName = search->currentFolder();
407  }
408 
409  if (search && !search->running()) {
410  procMsg = i18n("%n message searched", "%n messages searched",
411  numProcessed);
412  if(!mStopped) {
413  genMsg = i18n("Done.");
414  detailMsg = i18n("%n match in %1", "%n matches in %1",
415  numMatches).arg(procMsg);
416  } else {
417  genMsg = i18n("Search canceled.");
418  detailMsg = i18n("%n match so far in %1", "%n matches so far in %1",
419  numMatches).arg(procMsg);
420  }
421  } else {
422  procMsg = i18n("%n message", "%n messages", numProcessed);
423  genMsg = i18n("%n match", "%n matches", numMatches);
424  detailMsg = i18n("Searching in %1. %2 searched so far")
425  .arg(folderName).arg(procMsg);
426  }
427 
428  mStatusBar->changeItem(genMsg, 0);
429  mStatusBar->changeItem(detailMsg, 1);
430 }
431 
432 
433 //-----------------------------------------------------------------------------
434 void SearchWindow::keyPressEvent(TQKeyEvent *evt)
435 {
436  KMSearch const *search = (mFolder) ? mFolder->search() : 0;
437  bool searching = (search) ? search->running() : false;
438  if (evt->key() == Key_Escape && searching) {
439  mFolder->stopSearch();
440  return;
441  }
442 
443  KDialogBase::keyPressEvent(evt);
444 }
445 
446 
447 //-----------------------------------------------------------------------------
448 void SearchWindow::slotFolderActivated()
449 {
450  mChkbxSpecificFolders->setChecked(true);
451 }
452 
453 //-----------------------------------------------------------------------------
455 {
456  mChkbxSpecificFolders->setChecked(true);
457  mCbxFolders->setFolder(curFolder);
458 }
459 
460 //-----------------------------------------------------------------------------
461 void SearchWindow::slotSearch()
462 {
463  mLastFocus = focusWidget();
464  mBtnSearch->setFocus(); // set focus so we don't miss key event
465 
466  mStopped = false;
467  mFetchingInProgress = 0;
468 
469  mSearchFolderOpenBtn->setEnabled(true);
470  if ( mSearchFolderEdt->text().isEmpty() ) {
471  mSearchFolderEdt->setText( i18n("Last Search") );
472  }
473  mBtnSearch->setEnabled(false);
474  mBtnStop->setEnabled(true);
475 
476  mLbxMatches->clear();
477 
478  mSortColumn = mLbxMatches->sortColumn();
479  mSortOrder = mLbxMatches->sortOrder();
480  mLbxMatches->setSorting(-1);
481  mLbxMatches->setShowSortIndicator(false);
482 
483  // If we haven't openend an existing search folder, find or
484  // create one.
485  if (!mFolder) {
486  KMFolderMgr *mgr = kmkernel->searchFolderMgr();
487  TQString baseName = mSearchFolderEdt->text();
488  TQString fullName = baseName;
489  int count = 0;
490  KMFolder *folder;
491  while ((folder = mgr->find(fullName))) {
492  if (folder->storage()->inherits("KMFolderSearch"))
493  break;
494  fullName = TQString("%1 %2").arg(baseName).arg(++count);
495  }
496 
497  if (!folder)
498  folder = mgr->createFolder(fullName, false, KMFolderTypeSearch,
499  &mgr->dir());
500 
501  mFolder = dynamic_cast<KMFolderSearch*>( folder->storage() );
502  }
503  mFolder->stopSearch();
504  disconnect(mFolder, TQ_SIGNAL(msgAdded(int)),
505  this, TQ_SLOT(slotAddMsg(int)));
506  disconnect(mFolder, TQ_SIGNAL(msgRemoved(KMFolder*, TQ_UINT32)),
507  this, TQ_SLOT(slotRemoveMsg(KMFolder*, TQ_UINT32)));
508  connect(mFolder, TQ_SIGNAL(msgAdded(int)),
509  this, TQ_SLOT(slotAddMsg(int)));
510  connect(mFolder, TQ_SIGNAL(msgRemoved(KMFolder*, TQ_UINT32)),
511  this, TQ_SLOT(slotRemoveMsg(KMFolder*, TQ_UINT32)));
512  mSearchFolderEdt->setEnabled(false);
513  KMSearch *search = new KMSearch();
514  connect(search, TQ_SIGNAL(finished(bool)),
515  this, TQ_SLOT(searchDone()));
516  if (mChkbxAllFolders->isChecked()) {
517  search->setRecursive(true);
518  } else {
519  search->setRoot(mCbxFolders->folder());
520  search->setRecursive(mChkSubFolders->isChecked());
521  }
522 
523  mPatternEdit->updateSearchPattern();
524  KMSearchPattern *searchPattern = new KMSearchPattern();
525  *searchPattern = *mSearchPattern; //deep copy
526  searchPattern->purify();
527  search->setSearchPattern(searchPattern);
528  mFolder->setSearch(search);
529  enableGUI();
530 
531  mTimer->start(200);
532 }
533 
534 //-----------------------------------------------------------------------------
536 {
537  mTimer->stop();
538  updStatus();
539 
540  TQTimer::singleShot(0, this, TQ_SLOT(enableGUI()));
541  if(mLastFocus)
542  mLastFocus->setFocus();
543  if (mCloseRequested)
544  close();
545 
546  mLbxMatches->setSorting(mSortColumn, mSortOrder == Ascending);
547  mLbxMatches->setShowSortIndicator(true);
548 
549  mSearchFolderEdt->setEnabled(true);
550 }
551 
552 void SearchWindow::slotAddMsg(int idx)
553 {
554  if (!mFolder)
555  return;
556  bool unget = !mFolder->isMessage(idx);
557  KMMessage *msg = mFolder->getMsg(idx);
558  TQString from, fName;
559  KMFolder *pFolder = msg->parent();
560  if (!mFolders.contains(pFolder)) {
561  mFolders.append(pFolder);
562  pFolder->open("searchwindow");
563  }
564  if(pFolder->whoField() == "To")
565  from = msg->to();
566  else
567  from = msg->from();
568  if (pFolder->isSystemFolder())
569  fName = i18n(pFolder->name().utf8());
570  else
571  fName = pFolder->name();
572 
573  (void)new TDEListViewItem(mLbxMatches, mLbxMatches->lastItem(),
574  msg->subject(), from, msg->dateIsoStr(),
575  fName,
576  TQString::number(mFolder->serNum(idx)));
577  if (unget)
578  mFolder->unGetMsg(idx);
579 }
580 
581 void SearchWindow::slotRemoveMsg(KMFolder *, TQ_UINT32 serNum)
582 {
583  if (!mFolder)
584  return;
585  TQListViewItemIterator it(mLbxMatches);
586  while (it.current()) {
587  TQListViewItem *item = *it;
588  if (serNum == (*it)->text(MSGID_COLUMN).toUInt()) {
589  delete item;
590  return;
591  }
592  ++it;
593  }
594 }
595 
596 //-----------------------------------------------------------------------------
597 void SearchWindow::slotStop()
598 {
599  if (mFolder)
600  mFolder->stopSearch();
601  mStopped = true;
602  mBtnStop->setEnabled(false);
603 }
604 
605 //-----------------------------------------------------------------------------
606 void SearchWindow::slotClose()
607 {
608  accept();
609 }
610 
611 
612 //-----------------------------------------------------------------------------
613 void SearchWindow::closeEvent(TQCloseEvent *e)
614 {
615  if (mFolder && mFolder->search() && mFolder->search()->running()) {
616  mCloseRequested = true;
617  //Cancel search in progress by setting the search folder search to
618  //the null search
619  mFolder->setSearch(new KMSearch());
620  TQTimer::singleShot(0, this, TQ_SLOT(slotClose()));
621  } else {
622  KDialogBase::closeEvent(e);
623  }
624 }
625 
626 //-----------------------------------------------------------------------------
627 void SearchWindow::scheduleRename( const TQString &s)
628 {
629  if (!s.isEmpty() ) {
630  mRenameTimer.start(250, true);
631  mSearchFolderOpenBtn->setEnabled(false);
632  } else {
633  mRenameTimer.stop();
634  mSearchFolderOpenBtn->setEnabled(!s.isEmpty());
635  }
636 }
637 
638 //-----------------------------------------------------------------------------
639 void SearchWindow::renameSearchFolder()
640 {
641  if (mFolder && (mFolder->folder()->name() != mSearchFolderEdt->text())) {
642  int i = 1;
643  TQString name = mSearchFolderEdt->text();
644  while (i < 100) {
645  if (!kmkernel->searchFolderMgr()->find( name )) {
646  mFolder->rename( name );
647  kmkernel->searchFolderMgr()->contentsChanged();
648  break;
649  }
650  name.setNum( i );
651  name = mSearchFolderEdt->text() + " " + name;
652  ++i;
653  }
654  }
655  if ( mFolder )
656  mSearchFolderOpenBtn->setEnabled(true);
657 }
658 
659 void SearchWindow::openSearchFolder()
660 {
661  Q_ASSERT( mFolder );
662  renameSearchFolder();
663  mKMMainWidget->slotSelectFolder( mFolder->folder() );
664  slotClose();
665 }
666 
667 //-----------------------------------------------------------------------------
668 void SearchWindow::folderInvalidated(KMFolder *folder)
669 {
670  if (folder->storage() == mFolder) {
671  mLbxMatches->clear();
672  if (mFolder->search())
673  connect(mFolder->search(), TQ_SIGNAL(finished(bool)),
674  this, TQ_SLOT(searchDone()));
675  mTimer->start(200);
676  enableGUI();
677  }
678 }
679 
680 //-----------------------------------------------------------------------------
681 KMMessage *SearchWindow::indexToMessage( TQListViewItem *item )
682 {
683  if( !item ) {
684  return 0;
685  }
686 
687  KMFolder *folder;
688  int msgIndex;
689  KMMsgDict::instance()->getLocation( item->text( MSGID_COLUMN ).toUInt(),
690  &folder, &msgIndex );
691 
692  if ( !folder || msgIndex < 0 ) {
693  return 0;
694  }
695 
696  mKMMainWidget->slotSelectFolder( folder );
697  return folder->getMsg( msgIndex );
698 }
699 
700 //-----------------------------------------------------------------------------
701 bool SearchWindow::slotShowMsg( TQListViewItem *item )
702 {
703  KMMessage *message = indexToMessage( item );
704  if ( message ) {
705  mKMMainWidget->slotSelectMessage( message );
706  return true;
707  }
708  return false;
709 }
710 
711 //-----------------------------------------------------------------------------
712 void SearchWindow::slotViewSelectedMsg()
713 {
714  slotViewMsg( mLbxMatches->currentItem() );
715 }
716 
717 //-----------------------------------------------------------------------------
718 bool SearchWindow::slotViewMsg( TQListViewItem *item )
719 {
720  KMMessage *message = indexToMessage( item );
721  if ( message ) {
722  mKMMainWidget->slotMsgActivated( message );
723  return true;
724  }
725  return false;
726 }
727 
728 //-----------------------------------------------------------------------------
729 void SearchWindow::slotCurrentChanged(TQListViewItem *item)
730 {
731  mSearchResultOpenBtn->setEnabled(item!=0);
732 }
733 
734 //-----------------------------------------------------------------------------
735 void SearchWindow::enableGUI()
736 {
737  KMSearch const *search = (mFolder) ? (mFolder->search()) : 0;
738  bool searching = (search) ? (search->running()) : false;
739  actionButton(KDialogBase::Close)->setEnabled(!searching);
740  mCbxFolders->setEnabled(!searching && !mChkbxAllFolders->isChecked());
741  mChkSubFolders->setEnabled(!searching && !mChkbxAllFolders->isChecked());
742  mChkbxAllFolders->setEnabled(!searching);
743  mChkbxSpecificFolders->setEnabled(!searching);
744  mPatternEdit->setEnabled(!searching);
745  mBtnSearch->setEnabled(!searching);
746  mBtnStop->setEnabled(searching);
747 }
748 
749 
750 //-----------------------------------------------------------------------------
752 {
753  KMMessageList msgList;
754  KMFolder* folder = 0;
755  int msgIndex = -1;
756  for (TQListViewItemIterator it(mLbxMatches); it.current(); it++)
757  if (it.current()->isSelected()) {
758  KMMsgDict::instance()->getLocation((*it)->text(MSGID_COLUMN).toUInt(),
759  &folder, &msgIndex);
760  if (folder && msgIndex >= 0)
761  msgList.append(folder->getMsgBase(msgIndex));
762  }
763  return msgList;
764 }
765 
766 //-----------------------------------------------------------------------------
768 {
769  TQListViewItem *item = mLbxMatches->currentItem();
770  KMFolder* folder = 0;
771  int msgIndex = -1;
772  if (!item)
773  return 0;
774  KMMsgDict::instance()->getLocation(item->text(MSGID_COLUMN).toUInt(),
775  &folder, &msgIndex);
776  if (!folder || msgIndex < 0)
777  return 0;
778 
779  return folder->getMsg(msgIndex);
780 }
781 
782 //-----------------------------------------------------------------------------
783 void SearchWindow::moveSelectedToFolder( int menuId )
784 {
785  KMFolder *dest = mMenuToFolder[menuId];
786  if (!dest)
787  return;
788 
789  KMMessageList msgList = selectedMessages();
790  KMCommand *command = new KMMoveCommand( dest, msgList );
791  command->start();
792 }
793 
794 //-----------------------------------------------------------------------------
795 void SearchWindow::copySelectedToFolder( int menuId )
796 {
797  KMFolder *dest = mMenuToFolder[menuId];
798  if (!dest)
799  return;
800 
801  KMMessageList msgList = selectedMessages();
802  KMCommand *command = new KMCopyCommand( dest, msgList );
803  command->start();
804 }
805 
806 //-----------------------------------------------------------------------------
807 void SearchWindow::updateContextMenuActions()
808 {
809  int count = selectedMessages().count();
810  bool single_actions = count == 1;
811  mReplyAction->setEnabled( single_actions );
812  mReplyAllAction->setEnabled( single_actions );
813  mReplyListAction->setEnabled( single_actions );
814  mPrintAction->setEnabled( single_actions );
815  mForwardDigestAction->setEnabled( !single_actions );
816  mRedirectAction->setEnabled( single_actions );
817  mCopyAction->setEnabled( count > 0 );
818  mCutAction->setEnabled( count > 0 );
819 }
820 
821 //-----------------------------------------------------------------------------
822 void SearchWindow::slotContextMenuRequested( TQListViewItem *lvi, const TQPoint &, int )
823 {
824  if (!lvi)
825  return;
826  mLbxMatches->setSelected( lvi, true );
827  mLbxMatches->setCurrentItem( lvi );
828  // FIXME is this ever unGetMsg()'d?
829  if (!message())
830  return;
831  TQPopupMenu *menu = new TQPopupMenu(this);
832  updateContextMenuActions();
833 
834  mMenuToFolder.clear();
835  TQPopupMenu *msgMoveMenu = new TQPopupMenu(menu);
836  mKMMainWidget->folderTree()->folderToPopupMenu( KMFolderTree::MoveMessage,
837  this, &mMenuToFolder, msgMoveMenu );
838  TQPopupMenu *msgCopyMenu = new TQPopupMenu(menu);
839  mKMMainWidget->folderTree()->folderToPopupMenu( KMFolderTree::CopyMessage,
840  this, &mMenuToFolder, msgCopyMenu );
841 
842  // show most used actions
843  mReplyAction->plug(menu);
844  mReplyAllAction->plug(menu);
845  mReplyListAction->plug(menu);
846  mForwardActionMenu->plug(menu);
847  menu->insertSeparator();
848  mCopyAction->plug(menu);
849  mCutAction->plug(menu);
850  menu->insertItem(i18n("&Copy To"), msgCopyMenu);
851  menu->insertItem(i18n("&Move To"), msgMoveMenu);
852  menu->insertSeparator();
853  mSaveAsAction->plug(menu);
854  mSaveAtchAction->plug(menu);
855  mPrintAction->plug(menu);
856  menu->insertSeparator();
857  mClearAction->plug(menu);
858  menu->exec (TQCursor::pos(), 0);
859  delete menu;
860 }
861 
862 //-----------------------------------------------------------------------------
863 void SearchWindow::slotClearSelection()
864 {
865  mLbxMatches->clearSelection();
866 }
867 
868 //-----------------------------------------------------------------------------
869 void SearchWindow::slotReplyToMsg()
870 {
871  KMCommand *command = new KMReplyToCommand(this, message());
872  command->start();
873 }
874 
875 //-----------------------------------------------------------------------------
876 void SearchWindow::slotReplyAllToMsg()
877 {
878  KMCommand *command = new KMReplyToAllCommand(this, message());
879  command->start();
880 }
881 
882 //-----------------------------------------------------------------------------
883 void SearchWindow::slotReplyListToMsg()
884 {
885  KMCommand *command = new KMReplyListCommand(this, message());
886  command->start();
887 }
888 
889 //-----------------------------------------------------------------------------
890 void SearchWindow::slotForwardInlineMsg()
891 {
892  KMCommand *command = new KMForwardInlineCommand(this, selectedMessages());
893  command->start();
894 }
895 
896 //-----------------------------------------------------------------------------
897 void SearchWindow::slotForwardAttachedMsg()
898 {
899  KMCommand *command = new KMForwardAttachedCommand(this, selectedMessages());
900  command->start();
901 }
902 
903 //-----------------------------------------------------------------------------
904 void SearchWindow::slotForwardDigestMsg()
905 {
906  KMCommand *command = new KMForwardDigestCommand(this, selectedMessages());
907  command->start();
908 }
909 
910 //-----------------------------------------------------------------------------
911 void SearchWindow::slotRedirectMsg()
912 {
913  KMCommand *command = new KMRedirectCommand(this, message());
914  command->start();
915 }
916 
917 //-----------------------------------------------------------------------------
918 void SearchWindow::slotSaveMsg()
919 {
920  KMSaveMsgCommand *saveCommand = new KMSaveMsgCommand(this,
921  selectedMessages());
922  if (saveCommand->url().isEmpty())
923  delete saveCommand;
924  else
925  saveCommand->start();
926 }
927 //-----------------------------------------------------------------------------
928 void SearchWindow::slotSaveAttachments()
929 {
930  KMSaveAttachmentsCommand *saveCommand = new KMSaveAttachmentsCommand(this,
931  selectedMessages());
932  saveCommand->start();
933 }
934 
935 
936 //-----------------------------------------------------------------------------
937 void SearchWindow::slotPrintMsg()
938 {
939  KMCommand *command = new KMPrintCommand(this, message());
940  command->start();
941 }
942 
943 void SearchWindow::slotCopyMsgs()
944 {
945  TQValueList<TQ_UINT32> list = MessageCopyHelper::serNumListFromMsgList( selectedMessages() );
946  mKMMainWidget->headers()->setCopiedMessages( list, false );
947 }
948 
949 void SearchWindow::slotCutMsgs()
950 {
951  TQValueList<TQ_UINT32> list = MessageCopyHelper::serNumListFromMsgList( selectedMessages() );
952  mKMMainWidget->headers()->setCopiedMessages( list, true );
953 }
954 
955 
956 void SearchWindow::setSearchPattern( const KMSearchPattern& pattern )
957 {
958  *mSearchPattern = pattern;
959  mPatternEdit->setSearchPattern( mSearchPattern );
960 }
961 
962 } // namespace KMail
963 #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.
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