• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepim API Reference
  • KDE Home
  • Contact Us
 

kmail

  • sources
  • kde-4.12
  • kdepim
  • kmail
  • searchdialog
searchwindow.cpp
Go to the documentation of this file.
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  * Copyright (c) 2010 Till Adam <adam@kde.org>
6  * Copyright (c) 2011, 2012 Laurent Montel <montel@kde.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  */
23 
24 #include "searchwindow.h"
25 
26 #include "folderrequester.h"
27 #if !defined(NDEBUG)
28 #include "debug/searchdebugdialog.h"
29 #endif
30 #include "kmcommands.h"
31 #include "kmmainwidget.h"
32 #include "mailcommon/kernel/mailkernel.h"
33 #include "mailcommon/search/searchpatternedit.h"
34 #include "mailcommon/widgets/regexplineedit.h"
35 #include "searchdescriptionattribute.h"
36 #include "foldertreeview.h"
37 #include "kmsearchmessagemodel.h"
38 #include "kmsearchfilterproxymodel.h"
39 #include "searchpatternwarning.h"
40 #include "mailcommon/folderdialog/selectmulticollectiondialog.h"
41 
42 #include <Akonadi/CollectionModifyJob>
43 #include <Akonadi/EntityTreeView>
44 #include <akonadi/persistentsearchattribute.h>
45 #include <Akonadi/SearchCreateJob>
46 #include <Akonadi/ChangeRecorder>
47 #include <akonadi/standardactionmanager.h>
48 #include <Akonadi/EntityMimeTypeFilterModel>
49 #include <KActionMenu>
50 #include <KDebug>
51 #include <KDialogButtonBox>
52 #include <KIcon>
53 #include <KIconLoader>
54 #include <KLineEdit>
55 #include <kmime/kmime_message.h>
56 #include <KStandardAction>
57 #include <KStandardGuiItem>
58 #include <KWindowSystem>
59 #include <KMessageBox>
60 
61 #include <QCheckBox>
62 #include <QCloseEvent>
63 #include <QCursor>
64 #include <QHeaderView>
65 #include <QKeyEvent>
66 #include <QMenu>
67 #include <QRadioButton>
68 #include <QVBoxLayout>
69 
70 using namespace KPIM;
71 using namespace MailCommon;
72 
73 namespace KMail {
74 
75 SearchWindow::SearchWindow( KMMainWidget *widget, const Akonadi::Collection &collection )
76  : KDialog( 0 ),
77  mCloseRequested( false ),
78  mSortColumn( 0 ),
79  mSortOrder( Qt::AscendingOrder ),
80  mSearchJob( 0 ),
81  mResultModel( 0 ),
82  mKMMainWidget( widget ),
83  mAkonadiStandardAction( 0 )
84 {
85  setCaption( i18n( "Find Messages" ) );
86 
87  KWindowSystem::setIcons( winId(), qApp->windowIcon().pixmap( IconSize( KIconLoader::Desktop ),
88  IconSize( KIconLoader::Desktop ) ),
89  qApp->windowIcon().pixmap( IconSize( KIconLoader::Small ),
90  IconSize( KIconLoader::Small ) ) );
91 
92  QWidget *topWidget = new QWidget;
93  QVBoxLayout *lay = new QVBoxLayout;
94  lay->setMargin(0);
95  topWidget->setLayout(lay);
96  mSearchPatternWidget = new SearchPatternWarning;
97  lay->addWidget(mSearchPatternWidget);
98  setMainWidget( topWidget );
99 
100  QWidget *searchWidget = new QWidget( this );
101  mUi.setupUi( searchWidget );
102 
103  lay->addWidget(searchWidget);
104 
105 
106  setButtons( None );
107  mStartSearchGuiItem = KGuiItem( i18nc( "@action:button Search for messages", "&Search" ), QLatin1String("edit-find") );
108  mStopSearchGuiItem = KStandardGuiItem::stop();
109  mSearchButton = mUi.mButtonBox->addButton( mStartSearchGuiItem, QDialogButtonBox::ActionRole );
110  connect( mUi.mButtonBox, SIGNAL(rejected()), SLOT(slotClose()) );
111 #if !defined(NDEBUG)
112  QPushButton *debugButton = mUi.mButtonBox->addButton( i18n("Debug query"), QDialogButtonBox::ActionRole );
113  connect(debugButton, SIGNAL(clicked(bool)), SLOT(slotDebugQuery()));
114 #endif
115  searchWidget->layout()->setMargin( 0 );
116 
117  mUi.mCbxFolders->setMustBeReadWrite( false );
118  mUi.mCbxFolders->setNotAllowToCreateNewFolder( true );
119 
120  bool currentFolderIsSearchFolder = false;
121 
122  if ( !collection.hasAttribute<Akonadi::PersistentSearchAttribute>() ) {
123  // it's not a search folder, make a new search
124  mSearchPattern.append( SearchRule::createInstance( "Subject" ) );
125  mUi.mCbxFolders->setCollection( collection );
126  } else {
127  // it's a search folder
128  if ( collection.hasAttribute<Akonadi::SearchDescriptionAttribute>() ) {
129  currentFolderIsSearchFolder = true; // FIXME is there a better way to tell?
130 
131  const Akonadi::SearchDescriptionAttribute* searchDescription = collection.attribute<Akonadi::SearchDescriptionAttribute>();
132  mSearchPattern.deserialize( searchDescription->description() );
133 
134  const QList<Akonadi::Collection::Id> lst = searchDescription->listCollection();
135  if (!lst.isEmpty()) {
136  mUi.mChkMultiFolders->setChecked(true);
137  mCollectionId.clear();
138  Q_FOREACH (Akonadi::Collection::Id col, lst) {
139  mCollectionId.append(Akonadi::Collection(col));
140  }
141  } else {
142  const Akonadi::Collection col = searchDescription->baseCollection();
143  if ( col.isValid() ) {
144  mUi.mChkbxSpecificFolders->setChecked( true );
145  mUi.mCbxFolders->setCollection( col );
146  mUi.mChkSubFolders->setChecked( searchDescription->recursive() );
147  } else {
148  mUi.mChkbxAllFolders->setChecked( true );
149  mUi.mChkSubFolders->setChecked( searchDescription->recursive() );
150  }
151  }
152  } else {
153  // it's a search folder, but not one of ours, warn the user that we can't edit it
154  // FIXME show results, but disable edit GUI
155  kWarning() << "This search was not created with KMail. It cannot be edited within it.";
156  mSearchPattern.clear();
157  }
158  }
159 
160  mUi.mPatternEdit->setSearchPattern( &mSearchPattern );
161  connect( mUi.mPatternEdit, SIGNAL(returnPressed()),
162  this, SLOT(slotSearch()) );
163 
164  // enable/disable widgets depending on radio buttons:
165  connect( mUi.mChkbxAllFolders, SIGNAL(toggled(bool)),
166  this, SLOT(setEnabledSearchButton(bool)) );
167 
168  mUi.mLbxMatches->setXmlGuiClient( mKMMainWidget->guiClient() );
169 
170  /*
171  Default is to sort by date. TODO: Unfortunately this sorts *while*
172  inserting, which looks rather strange - the user cannot read
173  the results so far as they are constantly re-sorted --dnaber
174 
175  Sorting is now disabled when a search is started and reenabled
176  when it stops. Items are appended to the list. This not only
177  solves the above problem, but speeds searches with many hits
178  up considerably. - till
179 
180  TODO: subclass QTreeWidgetItem and do proper (and performant)
181  compare functions
182  */
183  mUi.mLbxMatches->setSortingEnabled( true );
184 
185  connect( mUi.mLbxMatches, SIGNAL(customContextMenuRequested(QPoint)),
186  this, SLOT(slotContextMenuRequested(QPoint)) );
187  connect( mUi.mLbxMatches, SIGNAL(clicked(Akonadi::Item)),
188  this, SLOT(slotShowMsg(Akonadi::Item)) );
189  connect( mUi.mLbxMatches, SIGNAL(doubleClicked(Akonadi::Item)),
190  this, SLOT(slotViewMsg(Akonadi::Item)) );
191  connect( mUi.mLbxMatches, SIGNAL(currentChanged(Akonadi::Item)),
192  this, SLOT(slotCurrentChanged(Akonadi::Item)) );
193  connect( mUi.selectMultipleFolders, SIGNAL(clicked()),
194  this, SLOT(slotSelectMultipleFolders()));
195 
196  connect( KMKernel::self()->folderCollectionMonitor(), SIGNAL(collectionStatisticsChanged(Akonadi::Collection::Id,Akonadi::CollectionStatistics)), this, SLOT(updateCollectionStatistic(Akonadi::Collection::Id,Akonadi::CollectionStatistics)) );
197 
198  if ( currentFolderIsSearchFolder ) {
199  mFolder = collection;
200  mUi.mSearchFolderEdt->setText( collection.name() );
201  Q_ASSERT ( !mResultModel );
202  createSearchModel();
203  } else {
204  mUi.mSearchFolderEdt->setText( i18n( "Last Search" ) );
205  // find last search and reuse it if possible
206  mFolder = CommonKernel->collectionFromId( GlobalSettings::lastSearchCollectionId() );
207  // when the last folder got renamed, create a new one
208  if ( mFolder.isValid() && mFolder.name() != mUi.mSearchFolderEdt->text() ) {
209  mFolder = Akonadi::Collection();
210  }
211  }
212 
213  connect( mUi.mSearchFolderEdt, SIGNAL(textChanged(QString)),
214  this, SLOT(scheduleRename(QString)) );
215  connect( &mRenameTimer, SIGNAL(timeout()),
216  this, SLOT(renameSearchFolder()) );
217  connect( mUi.mSearchFolderOpenBtn, SIGNAL(clicked()),
218  this, SLOT(openSearchFolder()) );
219 
220  connect( mUi.mSearchResultOpenBtn, SIGNAL(clicked()),
221  this, SLOT(slotViewSelectedMsg()) );
222 
223  const int mainWidth = GlobalSettings::self()->searchWidgetWidth();
224  const int mainHeight = GlobalSettings::self()->searchWidgetHeight();
225 
226  if ( mainWidth || mainHeight )
227  resize( mainWidth, mainHeight );
228 
229  connect( mSearchButton, SIGNAL(clicked()), SLOT(slotSearch()) );
230  connect( this, SIGNAL(finished()), this, SLOT(deleteLater()) );
231  connect( this, SIGNAL(closeClicked()),this,SLOT(slotClose()) );
232 
233  // give focus to the value field of the first search rule
234  RegExpLineEdit* r = mUi.mPatternEdit->findChild<RegExpLineEdit*>( QLatin1String("regExpLineEdit") );
235  if ( r )
236  r->setFocus();
237  else
238  kDebug() << "SearchWindow: regExpLineEdit not found";
239 
240  //set up actions
241  KActionCollection *ac = actionCollection();
242  mReplyAction = new KAction( KIcon( QLatin1String("mail-reply-sender") ), i18n( "&Reply..." ), this );
243  actionCollection()->addAction( QLatin1String("search_reply"), mReplyAction );
244  connect( mReplyAction, SIGNAL(triggered(bool)), SLOT(slotReplyToMsg()) );
245 
246  mReplyAllAction = new KAction( KIcon( QLatin1String("mail-reply-all") ), i18n( "Reply to &All..." ), this );
247  actionCollection()->addAction( QLatin1String("search_reply_all"), mReplyAllAction );
248  connect( mReplyAllAction, SIGNAL(triggered(bool)), SLOT(slotReplyAllToMsg()) );
249 
250  mReplyListAction = new KAction( KIcon( QLatin1String("mail-reply-list") ), i18n( "Reply to Mailing-&List..." ), this );
251  actionCollection()->addAction(QLatin1String( "search_reply_list"), mReplyListAction );
252  connect( mReplyListAction, SIGNAL(triggered(bool)), SLOT(slotReplyListToMsg()) );
253 
254  mForwardActionMenu = new KActionMenu( KIcon( QLatin1String("mail-forward") ), i18nc( "Message->", "&Forward" ), this );
255  actionCollection()->addAction( QLatin1String("search_message_forward"), mForwardActionMenu );
256  connect( mForwardActionMenu, SIGNAL(triggered(bool)), this, SLOT(slotForwardMsg()) );
257 
258  mForwardInlineAction = new KAction( KIcon( QLatin1String("mail-forward") ),
259  i18nc( "@action:inmenu Forward message inline.", "&Inline..." ),
260  this );
261  actionCollection()->addAction( QLatin1String("search_message_forward_inline"), mForwardInlineAction );
262  connect( mForwardInlineAction, SIGNAL(triggered(bool)), SLOT(slotForwardMsg()) );
263 
264  mForwardAttachedAction = new KAction( KIcon( QLatin1String("mail-forward") ), i18nc( "Message->Forward->", "As &Attachment..." ), this );
265  actionCollection()->addAction( QLatin1String("search_message_forward_as_attachment"), mForwardAttachedAction );
266  connect( mForwardAttachedAction, SIGNAL(triggered(bool)), SLOT(slotForwardAttachedMsg()) );
267 
268  if ( GlobalSettings::self()->forwardingInlineByDefault() ) {
269  mForwardActionMenu->addAction( mForwardInlineAction );
270  mForwardActionMenu->addAction( mForwardAttachedAction );
271  } else {
272  mForwardActionMenu->addAction( mForwardAttachedAction );
273  mForwardActionMenu->addAction( mForwardInlineAction );
274  }
275 
276  mSaveAsAction = actionCollection()->addAction( KStandardAction::SaveAs, QLatin1String("search_file_save_as"), this, SLOT(slotSaveMsg()) );
277 
278  mSaveAtchAction = new KAction( KIcon( QLatin1String("mail-attachment") ), i18n( "Save Attachments..." ), this );
279  actionCollection()->addAction( QLatin1String("search_save_attachments"), mSaveAtchAction );
280  connect( mSaveAtchAction, SIGNAL(triggered(bool)), SLOT(slotSaveAttachments()) );
281 
282  mPrintAction = actionCollection()->addAction( KStandardAction::Print, QLatin1String("search_print"), this, SLOT(slotPrintMsg()) );
283 
284  mClearAction = new KAction( i18n( "Clear Selection" ), this );
285  actionCollection()->addAction( QLatin1String("search_clear_selection"), mClearAction );
286  connect( mClearAction, SIGNAL(triggered(bool)), SLOT(slotClearSelection()) );
287 
288  connect( mUi.mCbxFolders, SIGNAL(folderChanged(Akonadi::Collection)),
289  this, SLOT(slotFolderActivated()) );
290 
291  ac->addAssociatedWidget( this );
292  foreach ( QAction* action, ac->actions() )
293  action->setShortcutContext( Qt::WidgetWithChildrenShortcut );
294 }
295 
296 SearchWindow::~SearchWindow()
297 {
298  if ( mResultModel ) {
299  if ( mUi.mLbxMatches->columnWidth( 0 ) > 0 ) {
300  GlobalSettings::self()->setCollectionWidth( mUi.mLbxMatches->columnWidth( 0 ) );
301  }
302  if ( mUi.mLbxMatches->columnWidth( 1 ) > 0 ) {
303  GlobalSettings::self()->setSubjectWidth( mUi.mLbxMatches->columnWidth( 1 ) );
304  }
305  if ( mUi.mLbxMatches->columnWidth( 2 ) > 0 ) {
306  GlobalSettings::self()->setSenderWidth( mUi.mLbxMatches->columnWidth( 2 ) );
307  }
308  if ( mUi.mLbxMatches->columnWidth( 3 ) > 0 ) {
309  GlobalSettings::self()->setReceiverWidth( mUi.mLbxMatches->columnWidth( 3 ) );
310  }
311  if ( mUi.mLbxMatches->columnWidth( 4 ) > 0 ) {
312  GlobalSettings::self()->setDateWidth( mUi.mLbxMatches->columnWidth( 4 ) );
313  }
314  if ( mUi.mLbxMatches->columnWidth( 5 ) > 0 ) {
315  GlobalSettings::self()->setFolderWidth( mUi.mLbxMatches->columnWidth( 5 ) );
316  }
317  GlobalSettings::self()->setSearchWidgetWidth( width() );
318  GlobalSettings::self()->setSearchWidgetHeight( height() );
319  GlobalSettings::self()->requestSync();
320  mResultModel->deleteLater();
321  }
322 }
323 
324 void SearchWindow::createSearchModel()
325 {
326  if ( mResultModel ) {
327  mResultModel->deleteLater();
328  }
329  mResultModel = new KMSearchMessageModel( this );
330  mResultModel->setCollection( mFolder );
331  KMSearchFilterProxyModel *sortproxy = new KMSearchFilterProxyModel( mResultModel );
332  sortproxy->setSourceModel( mResultModel );
333  mUi.mLbxMatches->setModel( sortproxy );
334 
335  mUi.mLbxMatches->setColumnWidth( 0, GlobalSettings::self()->collectionWidth() );
336  mUi.mLbxMatches->setColumnWidth( 1, GlobalSettings::self()->subjectWidth() );
337  mUi.mLbxMatches->setColumnWidth( 2, GlobalSettings::self()->senderWidth() );
338  mUi.mLbxMatches->setColumnWidth( 3, GlobalSettings::self()->receiverWidth() );
339  mUi.mLbxMatches->setColumnWidth( 4, GlobalSettings::self()->dateWidth() );
340  mUi.mLbxMatches->setColumnWidth( 5, GlobalSettings::self()->folderWidth() );
341  mUi.mLbxMatches->setColumnHidden( 6, true );
342  mUi.mLbxMatches->setColumnHidden( 7, true );
343  mUi.mLbxMatches->header()->setSortIndicator( 2, Qt::DescendingOrder );
344  mUi.mLbxMatches->header()->setStretchLastSection( false );
345  mUi.mLbxMatches->header()->restoreState( mHeaderState );
346  //mUi.mLbxMatches->header()->setResizeMode( 3, QHeaderView::Stretch );
347  if(!mAkonadiStandardAction)
348  mAkonadiStandardAction = new Akonadi::StandardMailActionManager( actionCollection(), this );
349  mAkonadiStandardAction->setItemSelectionModel( mUi.mLbxMatches->selectionModel() );
350  mAkonadiStandardAction->setCollectionSelectionModel( mKMMainWidget->folderTreeView()->selectionModel() );
351 
352 }
353 
354 
355 void SearchWindow::setEnabledSearchButton( bool )
356 {
357  //Make sure that button is enable
358  //Before when we selected a folder == "Local Folder" as that it was not a folder
359  //search button was disable, and when we select "Search in all local folder"
360  //Search button was never enabled :(
361  mSearchButton->setEnabled( true );
362 }
363 
364 void SearchWindow::updateCollectionStatistic(Akonadi::Collection::Id id,Akonadi::CollectionStatistics statistic)
365 {
366  QString genMsg;
367  if ( id == mFolder.id() ) {
368  genMsg = i18np( "%1 match", "%1 matches", statistic.count() );
369  }
370  mUi.mStatusLbl->setText( genMsg );
371 }
372 
373 void SearchWindow::keyPressEvent( QKeyEvent *event )
374 {
375  if ( event->key() == Qt::Key_Escape && mSearchJob ) {
376  slotStop();
377  return;
378  }
379 
380  KDialog::keyPressEvent( event );
381 }
382 
383 void SearchWindow::slotFolderActivated()
384 {
385  mUi.mChkbxSpecificFolders->setChecked( true );
386 }
387 
388 void SearchWindow::activateFolder( const Akonadi::Collection &collection )
389 {
390  mUi.mChkbxSpecificFolders->setChecked( true );
391  mUi.mCbxFolders->setCollection( collection );
392 }
393 
394 void SearchWindow::slotSearch()
395 {
396  mSearchPatternWidget->hideWarningPattern();
397  if ( mUi.mSearchFolderEdt->text().isEmpty() ) {
398  mUi.mSearchFolderEdt->setText( i18n( "Last Search" ) );
399  }
400 
401  if ( mResultModel )
402  mHeaderState = mUi.mLbxMatches->header()->saveState();
403 
404  mUi.mLbxMatches->setModel( 0 );
405 
406  mSortColumn = mUi.mLbxMatches->header()->sortIndicatorSection();
407  mSortOrder = mUi.mLbxMatches->header()->sortIndicatorOrder();
408  mUi.mLbxMatches->setSortingEnabled( false );
409 
410  if ( mSearchJob ) {
411  mSearchJob->kill( KJob::Quietly );
412  mSearchJob->deleteLater();
413  mSearchJob = 0;
414  }
415 
416  mUi.mSearchFolderEdt->setEnabled( false );
417 
418  KUrl::List urls;
419  if ( mUi.mChkbxSpecificFolders->isChecked() ) {
420  const Akonadi::Collection col = mUi.mCbxFolders->collection();
421  if (!col.isValid()) {
422  mSearchPatternWidget->showWarningPattern(QStringList()<<i18n("You did not selected a valid folder."));
423  mUi.mSearchFolderEdt->setEnabled( true );
424  return;
425  }
426  urls << col.url( Akonadi::Collection::UrlShort );
427  if ( mUi.mChkSubFolders->isChecked() ) {
428  childCollectionsFromSelectedCollection( col, urls );
429  }
430  } else if (mUi.mChkMultiFolders->isChecked()) {
431  if (!mSelectMultiCollectionDialog) {
432  return;
433  }
434  mCollectionId = mSelectMultiCollectionDialog->selectedCollection();
435  Q_FOREACH(const Akonadi::Collection &col, mCollectionId) {
436  urls << col.url( Akonadi::Collection::UrlShort );
437  }
438  if (urls.isEmpty()) {
439  mUi.mSearchFolderEdt->setEnabled( true );
440  mSearchPatternWidget->showWarningPattern(QStringList()<<i18n("You forgot to select collections."));
441  mQuery.clear();
442  return;
443  }
444  }
445 
446  mUi.mPatternEdit->updateSearchPattern();
447 
448  SearchPattern searchPattern( mSearchPattern );
449  searchPattern.purify();
450 
451  MailCommon::SearchPattern::SparqlQueryError queryError = MailCommon::SearchPattern::NoError;
452  queryError = searchPattern.asSparqlQuery(mQuery, urls);
453  const QString queryLanguage = QLatin1String("SPARQL");
454 
455  switch(queryError) {
456  case MailCommon::SearchPattern::NoError:
457  break;
458  case MailCommon::SearchPattern::MissingCheck:
459  mUi.mSearchFolderEdt->setEnabled( true );
460  mSearchPatternWidget->showWarningPattern(QStringList()<<i18n("You forgot to define condition."));
461  mQuery.clear();
462  return;
463  case MailCommon::SearchPattern::FolderEmptyOrNotIndexed:
464  mUi.mSearchFolderEdt->setEnabled( true );
465  mSearchPatternWidget->showWarningPattern(QStringList()<<i18n("All folders selected are empty or were not indexed."));
466  mQuery.clear();
467  return;
468  case MailCommon::SearchPattern::EmptyResult:
469  mUi.mSearchFolderEdt->setEnabled( true );
470  mQuery.clear();
471  mUi.mStatusLbl->setText( i18n("No message found.") );
472  createSearchModel();
473  return;
474  case MailCommon::SearchPattern::NotEnoughCharacters:
475  mUi.mSearchFolderEdt->setEnabled( true );
476  mSearchPatternWidget->showWarningPattern(QStringList()<<i18n("Contains condition cannot be used with a number of characters inferior to 4."));
477  mQuery.clear();
478  return;
479  }
480  mSearchPatternWidget->hideWarningPattern();
481  qDebug() << queryLanguage;
482  qDebug() << mQuery;
483  mUi.mSearchFolderOpenBtn->setEnabled( true );
484 
485  if ( !mFolder.isValid() ) {
486  qDebug()<<" create new folder";
487  // FIXME if another app created a virtual 'Last Search' folder without
488  // out custom attributes it will result in problems
489  mSearchJob = new Akonadi::SearchCreateJob( mUi.mSearchFolderEdt->text(), mQuery, this );
490  } else {
491  qDebug()<<" use existing folder";
492  Akonadi::PersistentSearchAttribute *attribute = mFolder.attribute<Akonadi::PersistentSearchAttribute>();
493  attribute->setQueryLanguage( queryLanguage );
494  attribute->setQueryString( mQuery );
495  mSearchJob = new Akonadi::CollectionModifyJob( mFolder, this );
496  }
497 
498  connect( mSearchJob, SIGNAL(result(KJob*)), SLOT(searchDone(KJob*)) );
499  enableGUI();
500  mUi.mStatusLbl->setText( i18n( "Searching..." ) );
501 }
502 
503 void SearchWindow::searchDone( KJob* job )
504 {
505  Q_ASSERT( job == mSearchJob );
506  QMetaObject::invokeMethod( this, "enableGUI", Qt::QueuedConnection );
507  if ( job->error() ) {
508  KMessageBox::sorry( this, i18n("Cannot get search result. %1", job->errorString() ) );
509  if ( mSearchJob ) {
510  mSearchJob = 0;
511  }
512  enableGUI();
513  mUi.mSearchFolderEdt->setEnabled( true );
514  }
515  else
516  {
517  if ( Akonadi::SearchCreateJob *searchJob = qobject_cast<Akonadi::SearchCreateJob*>( mSearchJob ) ) {
518  mFolder = searchJob->createdCollection();
519  } else if ( Akonadi::CollectionModifyJob *modifyJob = qobject_cast<Akonadi::CollectionModifyJob*>( mSearchJob ) ) {
520  mFolder = modifyJob->collection();
521  }
523  Q_ASSERT( mFolder.isValid() );
524  Q_ASSERT( mFolder.hasAttribute<Akonadi::PersistentSearchAttribute>() );
525 
526  GlobalSettings::setLastSearchCollectionId( mFolder.id() );
527  GlobalSettings::self()->writeConfig();
528  GlobalSettings::self()->requestSync();
529 
530  // store the kmail specific serialization of the search in an attribute on
531  // the server, for easy retrieval when editing it again
532  const QByteArray search = mSearchPattern.serialize();
533  Q_ASSERT( !search.isEmpty() );
534  Akonadi::SearchDescriptionAttribute *searchDescription = mFolder.attribute<Akonadi::SearchDescriptionAttribute>( Akonadi::Entity::AddIfMissing );
535  searchDescription->setDescription( search );
536  if ( mUi.mChkMultiFolders->isChecked()) {
537  searchDescription->setBaseCollection( Akonadi::Collection() );
538  QList<Akonadi::Collection::Id> lst;
539  Q_FOREACH (const Akonadi::Collection &col, mCollectionId) {
540  lst << col.id();
541  }
542  searchDescription->setListCollection(lst);
543  } else if (mUi.mChkbxSpecificFolders->isChecked()) {
544  const Akonadi::Collection collection = mUi.mCbxFolders->collection();
545  searchDescription->setBaseCollection( collection );
546  } else {
547  searchDescription->setBaseCollection( Akonadi::Collection() );
548  }
549  searchDescription->setRecursive( mUi.mChkSubFolders->isChecked() );
550  new Akonadi::CollectionModifyJob( mFolder, this );
551  mSearchJob = 0;
552 
553  mUi.mStatusLbl->setText( QString() );
554  createSearchModel();
555 
556  if ( mCloseRequested )
557  close();
558 
559  mUi.mLbxMatches->setSortingEnabled( true );
560  mUi.mLbxMatches->header()->setSortIndicator( mSortColumn, mSortOrder );
561 
562  mUi.mSearchFolderEdt->setEnabled( true );
563  }
564 }
565 
566 void SearchWindow::slotStop()
567 {
568  if ( mSearchJob ) {
569  mSearchJob->kill( KJob::Quietly );
570  mSearchJob->deleteLater();
571  mSearchJob = 0;
572  }
573 
574  enableGUI();
575 }
576 
577 void SearchWindow::slotClose()
578 {
579  accept();
580 }
581 
582 void SearchWindow::closeEvent( QCloseEvent *event )
583 {
584  if ( mSearchJob ) {
585  mCloseRequested = true;
586  //Cancel search in progress
587  mSearchJob->kill( KJob::Quietly );
588  mSearchJob->deleteLater();
589  mSearchJob = 0;
590  QTimer::singleShot( 0, this, SLOT(slotClose()) );
591  } else {
592  KDialog::closeEvent( event );
593  }
594 }
595 
596 void SearchWindow::scheduleRename( const QString &text )
597 {
598  if ( !text.isEmpty() ) {
599  mRenameTimer.setSingleShot( true );
600  mRenameTimer.start( 250 );
601  mUi.mSearchFolderOpenBtn->setEnabled( false );
602  } else {
603  mRenameTimer.stop();
604  mUi.mSearchFolderOpenBtn->setEnabled( !text.isEmpty() );
605  }
606 }
607 
608 void SearchWindow::renameSearchFolder()
609 {
610  const QString name = mUi.mSearchFolderEdt->text();
611  if ( mFolder.isValid() ) {
612  const QString oldFolderName = mFolder.name();
613  if ( oldFolderName != name ) {
614  mFolder.setName( name );
615  Akonadi::CollectionModifyJob *job = new Akonadi::CollectionModifyJob( mFolder, this );
616  job->setProperty("oldfoldername", oldFolderName);
617  connect( job, SIGNAL(result(KJob*)),
618  this, SLOT(slotSearchFolderRenameDone(KJob*)) );
619  }
620  mUi.mSearchFolderOpenBtn->setEnabled( true );
621  }
622 }
623 
624 void SearchWindow::slotSearchFolderRenameDone( KJob *job )
625 {
626  Q_ASSERT( job );
627  if ( job->error() ) {
628  kWarning() << "Job failed:" << job->errorText();
629  KMessageBox::information( this, i18n( "There was a problem renaming your search folder. "
630  "A common reason for this is that another search folder "
631  "with the same name already exists. Error returned \"%1\".", job->errorText() ) );
632  mUi.mSearchFolderEdt->blockSignals(true);
633  mUi.mSearchFolderEdt->setText(job->property("oldfoldername").toString());
634  mUi.mSearchFolderEdt->blockSignals(false);
635  }
636 }
637 
638 void SearchWindow::openSearchFolder()
639 {
640  Q_ASSERT( mFolder.isValid() );
641  renameSearchFolder();
642  mKMMainWidget->slotSelectCollectionFolder( mFolder );
643  slotClose();
644 }
645 
646 bool SearchWindow::slotShowMsg( const Akonadi::Item &item )
647 {
648  if ( item.isValid() ) {
649  mKMMainWidget->slotMessageSelected( item );
650  return true;
651  }
652  return false;
653 }
654 
655 void SearchWindow::slotViewSelectedMsg()
656 {
657  mKMMainWidget->slotMessageActivated( selectedMessage() );
658 }
659 
660 bool SearchWindow::slotViewMsg( const Akonadi::Item &item )
661 {
662  if ( item.isValid() ) {
663  mKMMainWidget->slotMessageActivated( item );
664  return true;
665  }
666  return false;
667 }
668 
669 void SearchWindow::slotCurrentChanged( const Akonadi::Item &item )
670 {
671  mUi.mSearchResultOpenBtn->setEnabled( item.isValid() );
672 }
673 
674 void SearchWindow::enableGUI()
675 {
676  const bool searching = (mSearchJob != 0);
677 
678  mSearchButton->setGuiItem( searching ? mStopSearchGuiItem : mStartSearchGuiItem );
679  if ( searching ) {
680  disconnect( mSearchButton, SIGNAL(clicked()), this, SLOT(slotSearch()) );
681  connect( mSearchButton, SIGNAL(clicked()), SLOT(slotStop()) );
682  } else {
683  disconnect( mSearchButton, SIGNAL(clicked()), this, SLOT(slotStop()) );
684  connect( mSearchButton, SIGNAL(clicked()), SLOT(slotSearch()) );
685  }
686 }
687 
688 Akonadi::Item::List SearchWindow::selectedMessages() const
689 {
690  Akonadi::Item::List messages;
691 
692  foreach ( const QModelIndex &index, mUi.mLbxMatches->selectionModel()->selectedRows() ) {
693  const Akonadi::Item item = index.data( Akonadi::ItemModel::ItemRole ).value<Akonadi::Item>();
694  if ( item.isValid() )
695  messages.append( item );
696  }
697 
698  return messages;
699 }
700 
701 Akonadi::Item SearchWindow::selectedMessage() const
702 {
703  return mUi.mLbxMatches->currentIndex().data( Akonadi::ItemModel::ItemRole ).value<Akonadi::Item>();
704 }
705 
706 void SearchWindow::updateContextMenuActions()
707 {
708  const int count = selectedMessages().count();
709  const bool singleActions = (count == 1);
710 
711  mReplyAction->setEnabled( singleActions );
712  mReplyAllAction->setEnabled( singleActions );
713  mReplyListAction->setEnabled( singleActions );
714  mPrintAction->setEnabled( singleActions );
715  mSaveAtchAction->setEnabled( count > 0 );
716  mSaveAsAction->setEnabled( count > 0 );
717  mClearAction->setEnabled( count > 0 );
718 }
719 
720 void SearchWindow::slotContextMenuRequested( const QPoint& )
721 {
722  if ( !selectedMessage().isValid() || selectedMessages().isEmpty() )
723  return;
724 
725  QMenu *menu = new QMenu( this );
726  updateContextMenuActions();
727 
728  // show most used actions
729  menu->addAction( mReplyAction );
730  menu->addAction( mReplyAllAction );
731  menu->addAction( mReplyListAction );
732  menu->addAction( mForwardActionMenu );
733  menu->addSeparator();
734  KAction *act = mAkonadiStandardAction->createAction( Akonadi::StandardActionManager::CopyItems );
735  mAkonadiStandardAction->setActionText( Akonadi::StandardActionManager::CopyItems, ki18np( "Copy Message", "Copy %1 Messages" ) );
736  menu->addAction( act );
737  act = mAkonadiStandardAction->createAction( Akonadi::StandardActionManager::CutItems );
738  mAkonadiStandardAction->setActionText( Akonadi::StandardActionManager::CutItems, ki18np( "Cut Message", "Cut %1 Messages" ) );
739  menu->addAction( act );
740  menu->addAction( mAkonadiStandardAction->createAction( Akonadi::StandardActionManager::CopyItemToMenu ) );
741  menu->addAction( mAkonadiStandardAction->createAction( Akonadi::StandardActionManager::MoveItemToMenu ) );
742  menu->addSeparator();
743  menu->addAction( mSaveAsAction );
744  menu->addAction( mSaveAtchAction );
745  menu->addAction( mPrintAction );
746  menu->addSeparator();
747  menu->addAction( mClearAction );
748  menu->exec( QCursor::pos(), 0 );
749 
750  delete menu;
751 }
752 
753 void SearchWindow::slotClearSelection()
754 {
755  mUi.mLbxMatches->clearSelection();
756 }
757 
758 void SearchWindow::slotReplyToMsg()
759 {
760  KMCommand *command = new KMReplyCommand( this, selectedMessage(), MessageComposer::ReplySmart );
761  command->start();
762 }
763 
764 void SearchWindow::slotReplyAllToMsg()
765 {
766  KMCommand *command = new KMReplyCommand( this, selectedMessage(),MessageComposer::ReplyAll );
767  command->start();
768 }
769 
770 void SearchWindow::slotReplyListToMsg()
771 {
772  KMCommand *command = new KMReplyCommand( this, selectedMessage(),MessageComposer::ReplyList );
773  command->start();
774 }
775 
776 void SearchWindow::slotForwardMsg()
777 {
778  KMCommand *command = new KMForwardCommand( this, selectedMessages() );
779  command->start();
780 }
781 
782 void SearchWindow::slotForwardAttachedMsg()
783 {
784  KMCommand *command = new KMForwardAttachedCommand( this, selectedMessages() );
785  command->start();
786 }
787 
788 void SearchWindow::slotSaveMsg()
789 {
790  KMSaveMsgCommand *saveCommand = new KMSaveMsgCommand( this, selectedMessages() );
791  saveCommand->start();
792 }
793 
794 void SearchWindow::slotSaveAttachments()
795 {
796  KMSaveAttachmentsCommand *saveCommand = new KMSaveAttachmentsCommand( this, selectedMessages() );
797  saveCommand->start();
798 }
799 
800 void SearchWindow::slotPrintMsg()
801 {
802  KMCommand *command = new KMPrintCommand( this, selectedMessage() );
803  command->start();
804 }
805 
806 void SearchWindow::addRulesToSearchPattern( const SearchPattern &pattern )
807 {
808  SearchPattern p( mSearchPattern );
809  p.purify();
810 
811  QList<SearchRule::Ptr>::const_iterator it;
812  QList<SearchRule::Ptr>::const_iterator end(pattern.constEnd() ) ;
813 
814  for ( it = pattern.constBegin() ; it != end ; ++it ) {
815  p.append( SearchRule::createInstance( **it ) );
816  }
817 
818  mSearchPattern = p;
819  mUi.mPatternEdit->setSearchPattern( &mSearchPattern );
820 }
821 
822 void SearchWindow::childCollectionsFromSelectedCollection( const Akonadi::Collection& collection, KUrl::List&lstUrlCollection )
823 {
824  if ( collection.isValid() ) {
825  QModelIndex idx = Akonadi::EntityTreeModel::modelIndexForCollection( KMKernel::self()->collectionModel(), collection );
826  if ( idx.isValid() ) {
827  getChildren( KMKernel::self()->collectionModel(), idx, lstUrlCollection );
828  }
829  }
830 }
831 
832 void SearchWindow::getChildren( const QAbstractItemModel *model,
833  const QModelIndex &parentIndex,
834  KUrl::List &list )
835 {
836  const int rowCount = model->rowCount( parentIndex );
837  for ( int row = 0; row < rowCount; ++row ) {
838  const QModelIndex index = model->index( row, 0, parentIndex );
839  if ( model->rowCount( index ) > 0 ) {
840  getChildren( model, index, list );
841  }
842  Akonadi::Collection c = model->data(index, Akonadi::EntityTreeModel::CollectionRole ).value<Akonadi::Collection>();
843  if ( c.isValid() )
844  list << c.url( Akonadi::Collection::UrlShort );
845  }
846 }
847 
848 void SearchWindow::slotDebugQuery()
849 {
850 #if !defined(NDEBUG)
851  QPointer<SearchDebugDialog> dlg = new SearchDebugDialog(mQuery, this);
852  dlg->exec();
853  delete dlg;
854 #endif
855 }
856 
857 void SearchWindow::slotSelectMultipleFolders()
858 {
859  mUi.mChkMultiFolders->setChecked(true);
860  if (!mSelectMultiCollectionDialog) {
861  QList<Akonadi::Collection::Id> lst;
862  Q_FOREACH (const Akonadi::Collection &col, mCollectionId) {
863  lst << col.id();
864  }
865  mSelectMultiCollectionDialog = new MailCommon::SelectMultiCollectionDialog(lst, this);
866  }
867  mSelectMultiCollectionDialog->show();
868 }
869 
870 }
871 
872 #include "searchwindow.moc"
Akonadi::SearchDescriptionAttribute::description
QByteArray description() const
Definition: searchdescriptionattribute.cpp:66
KMail::SearchWindow::selectedMessage
Akonadi::Item selectedMessage() const
Provides access to the currently selected message.
Definition: searchwindow.cpp:701
KMReplyCommand
Definition: kmcommands.h:312
Akonadi::SearchDescriptionAttribute::setDescription
void setDescription(const QByteArray &desc)
Definition: searchdescriptionattribute.cpp:71
kmmainwidget.h
searchpatternwarning.h
searchdebugdialog.h
text
virtual QByteArray text(quint32 serialNumber) const =0
QWidget
KMMainWidget::slotMessageSelected
void slotMessageSelected(const Akonadi::Item &)
Opens mail in the internal viewer.
Definition: kmmainwidget.cpp:4462
KMMainWidget::slotMessageActivated
void slotMessageActivated(const Akonadi::Item &)
Open a separate viewer window containing the specified message.
Definition: kmmainwidget.cpp:2698
KDialog
KMail::SearchWindow::selectedMessages
Akonadi::Item::List selectedMessages() const
Provides access to the list of currently selected message in the listview.
Definition: searchwindow.cpp:688
KMKernel::self
static KMKernel * self()
normal control stuff
Definition: kmkernel.cpp:1451
searchwindow.h
KMMainWidget::folderTreeView
MailCommon::FolderTreeView * folderTreeView() const
Definition: kmmainwidget.h:140
KMSaveAttachmentsCommand
Definition: kmcommands.h:292
Akonadi::SearchDescriptionAttribute::recursive
bool recursive() const
Definition: searchdescriptionattribute.cpp:86
KMail::SearchWindow::closeEvent
void closeEvent(QCloseEvent *)
Reimplemented to stop searching when the window is closed.
Definition: searchwindow.cpp:582
KMCommand
Small helper structure which encapsulates the KMMessage created when creating a reply, and.
Definition: kmcommands.h:44
Akonadi::SearchDescriptionAttribute::baseCollection
Akonadi::Collection baseCollection() const
Definition: searchdescriptionattribute.cpp:76
KMCommand::start
void start()
Definition: kmcommands.cpp:234
Akonadi::SearchDescriptionAttribute
Definition: searchdescriptionattribute.h:30
KMail::SearchWindow::createSearchModel
void createSearchModel()
Definition: searchwindow.cpp:324
KMMainWidget
Definition: kmmainwidget.h:83
KMail::SearchWindow::addRulesToSearchPattern
void addRulesToSearchPattern(const MailCommon::SearchPattern &pattern)
Loads a search pattern into the search window, appending its rules to the current one...
Definition: searchwindow.cpp:806
kmsearchmessagemodel.h
KMail::KMSearchFilterProxyModel
Definition: kmsearchfilterproxymodel.h:37
KMail::SearchWindow::~SearchWindow
~SearchWindow()
Destroys the search window.
Definition: searchwindow.cpp:296
KMail::SearchWindow::getChildren
void getChildren(const QAbstractItemModel *model, const QModelIndex &parentIndex, KUrl::List &list)
Definition: searchwindow.cpp:832
KMForwardAttachedCommand
Definition: kmcommands.h:348
GlobalSettings::requestSync
void requestSync()
Call this slot instead of directly KConfig::sync() to minimize the overall config writes...
Definition: globalsettings.cpp:47
KMMainWidget::slotSelectCollectionFolder
void slotSelectCollectionFolder(const Akonadi::Collection &col)
Definition: kmmainwidget.cpp:2294
QMenu
KMForwardCommand
Definition: kmcommands.h:329
GlobalSettings::self
static GlobalSettings * self()
Definition: globalsettings.cpp:30
KMail::SearchPatternWarning
Definition: searchpatternwarning.h:25
KMail::SearchWindow::keyPressEvent
void keyPressEvent(QKeyEvent *)
Reimplemented to react to Escape.
Definition: searchwindow.cpp:373
KMSearchMessageModel
Definition: kmsearchmessagemodel.h:33
KMail::SearchWindow::childCollectionsFromSelectedCollection
void childCollectionsFromSelectedCollection(const Akonadi::Collection &collection, KUrl::List &list)
Definition: searchwindow.cpp:822
Akonadi::SearchDescriptionAttribute::listCollection
QList< Akonadi::Collection::Id > listCollection() const
Definition: searchdescriptionattribute.cpp:101
SearchDebugDialog
Definition: searchdebugdialog.h:24
KMPrintCommand
Definition: kmcommands.h:377
KMail::SearchPatternWarning::showWarningPattern
void showWarningPattern(const QStringList &lstError)
Definition: searchpatternwarning.cpp:41
KMail::SearchPatternWarning::hideWarningPattern
void hideWarningPattern()
Definition: searchpatternwarning.cpp:47
kmsearchfilterproxymodel.h
kmcommands.h
KMSaveMsgCommand
Definition: kmcommands.h:255
KJob
KMMainWidget::guiClient
KXMLGUIClient * guiClient() const
Returns the XML GUI client.
Definition: kmmainwidget.h:145
KMail::SearchWindow::activateFolder
void activateFolder(const Akonadi::Collection &folder)
Changes the base folder for search operations to a different folder.
Definition: searchwindow.cpp:388
QList< Akonadi::Collection::Id >
searchdescriptionattribute.h
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:58:52 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kmail

Skip menu "kmail"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdepim API Reference

Skip menu "kdepim API Reference"
  • akonadi_next
  • akregator
  • blogilo
  • calendarsupport
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt2
  • kjots
  • kleopatra
  • kmail
  • knode
  • knotes
  • kontact
  • korgac
  • korganizer
  • ktimetracker
  • libkdepim
  • libkleo
  • libkpgp
  • mailcommon
  • messagelist
  • messageviewer

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal