2 * find.cpp - search facility
4 * Copyright © 2005-2013 by David Jarvie <djarvie@kde.org>
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.
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.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include "alarmlistview.h"
25 #include "eventlistview.h"
26 #include "messagebox.h"
27 #include "preferences.h"
28 #include "config-kdepim.h"
30 #include <kalarmcal/kaevent.h>
32 #include <kfinddialog.h>
34 #include <kseparator.h>
35 #include <kwindowsystem.h>
36 #include <KLocalizedString>
40 #include <QVBoxLayout>
41 #include <QGridLayout>
44 #include <QApplication>
45 #include "kalarm_debug.h"
47 using namespace KAlarmCal
;
49 // KAlarm-specific options for Find dialog
51 FIND_LIVE
= KFind::MinimumUserOption
,
52 FIND_ARCHIVED
= KFind::MinimumUserOption
<< 1,
53 FIND_MESSAGE
= KFind::MinimumUserOption
<< 2,
54 FIND_FILE
= KFind::MinimumUserOption
<< 3,
55 FIND_COMMAND
= KFind::MinimumUserOption
<< 4,
56 FIND_EMAIL
= KFind::MinimumUserOption
<< 5,
57 FIND_AUDIO
= KFind::MinimumUserOption
<< 6
59 static long FIND_KALARM_OPTIONS
= FIND_LIVE
| FIND_ARCHIVED
| FIND_MESSAGE
| FIND_FILE
| FIND_COMMAND
| FIND_EMAIL
| FIND_AUDIO
;
62 Find::Find(EventListView
* parent
)
70 connect(mListView
->selectionModel(), &QItemSelectionModel::currentChanged
, this, &Find::slotSelectionChanged
);
75 delete mDialog
; // automatically set to 0
80 void Find::slotSelectionChanged()
83 mDialog
->setHasCursor(mListView
->selectionModel()->currentIndex().isValid());
86 /******************************************************************************
87 * Display the Find dialog.
92 // Set defaults the first time the Find dialog is activated
93 mOptions
= FIND_LIVE
| FIND_ARCHIVED
| FIND_MESSAGE
| FIND_FILE
| FIND_COMMAND
| FIND_EMAIL
| FIND_AUDIO
;
94 bool noArchived
= !Preferences::archivedKeepDays();
95 bool showArchived
= qobject_cast
<AlarmListView
*>(mListView
)
96 && (static_cast<AlarmListModel
*>(mListView
->model())->eventTypeFilter() & CalEvent::ARCHIVED
);
97 if (noArchived
|| !showArchived
) // these settings could change between activations
98 mOptions
&= ~FIND_ARCHIVED
;
103 KWindowSystem::activateWindow(mDialog
->winId());
108 mDialog
= new KFindDialog(mListView
, mOptions
, mHistory
, (mListView
->selectionModel()->selectedRows().count() > 1));
109 mDialog
->setModal(false);
110 mDialog
->setObjectName(QStringLiteral("FindDlg"));
111 mDialog
->setHasSelection(false);
112 QWidget
* kalarmWidgets
= mDialog
->findExtension();
115 QVBoxLayout
* layout
= new QVBoxLayout(kalarmWidgets
);
116 layout
->setMargin(0);
117 layout
->setSpacing(qApp
->style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
118 QGroupBox
* group
= new QGroupBox(i18nc("@title:group", "Alarm Type"), kalarmWidgets
);
119 layout
->addWidget(group
);
120 QGridLayout
* grid
= new QGridLayout(group
);
121 grid
->setMargin(qApp
->style()->pixelMetric(QStyle::PM_DefaultChildMargin
));
122 grid
->setSpacing(qApp
->style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
123 grid
->setColumnStretch(1, 1);
125 // Live & archived alarm selection
126 mLive
= new QCheckBox(i18nc("@option:check Alarm type", "Active"), group
);
127 mLive
->setFixedSize(mLive
->sizeHint());
128 mLive
->setWhatsThis(i18nc("@info:whatsthis", "Check to include active alarms in the search."));
129 grid
->addWidget(mLive
, 1, 0, Qt::AlignLeft
);
131 mArchived
= new QCheckBox(i18nc("@option:check Alarm type", "Archived"), group
);
132 mArchived
->setFixedSize(mArchived
->sizeHint());
133 mArchived
->setWhatsThis(i18nc("@info:whatsthis", "Check to include archived alarms in the search. "
134 "This option is only available if archived alarms are currently being displayed."));
135 grid
->addWidget(mArchived
, 1, 2, Qt::AlignLeft
);
137 mActiveArchivedSep
= new KSeparator(Qt::Horizontal
, kalarmWidgets
);
138 grid
->addWidget(mActiveArchivedSep
, 2, 0, 1, 3);
141 mMessageType
= new QCheckBox(i18nc("@option:check Alarm action = text display", "Text"), group
);
142 mMessageType
->setFixedSize(mMessageType
->sizeHint());
143 mMessageType
->setWhatsThis(i18nc("@info:whatsthis", "Check to include text message alarms in the search."));
144 grid
->addWidget(mMessageType
, 3, 0);
146 mFileType
= new QCheckBox(i18nc("@option:check Alarm action = file display", "File"), group
);
147 mFileType
->setFixedSize(mFileType
->sizeHint());
148 mFileType
->setWhatsThis(i18nc("@info:whatsthis", "Check to include file alarms in the search."));
149 grid
->addWidget(mFileType
, 3, 2);
151 mCommandType
= new QCheckBox(i18nc("@option:check Alarm action", "Command"), group
);
152 mCommandType
->setFixedSize(mCommandType
->sizeHint());
153 mCommandType
->setWhatsThis(i18nc("@info:whatsthis", "Check to include command alarms in the search."));
154 grid
->addWidget(mCommandType
, 4, 0);
156 mEmailType
= new QCheckBox(i18nc("@option:check Alarm action", "Email"), group
);
157 mEmailType
->setFixedSize(mEmailType
->sizeHint());
158 mEmailType
->setWhatsThis(i18nc("@info:whatsthis", "Check to include email alarms in the search."));
159 grid
->addWidget(mEmailType
, 4, 2);
161 mAudioType
= new QCheckBox(i18nc("@option:check Alarm action", "Audio"), group
);
162 mAudioType
->setFixedSize(mAudioType
->sizeHint());
163 mAudioType
->setWhatsThis(i18nc("@info:whatsthis", "Check to include audio alarms in the search."));
164 grid
->addWidget(mAudioType
, 5, 0);
167 mLive
->setChecked(mOptions
& FIND_LIVE
);
168 mArchived
->setChecked(mOptions
& FIND_ARCHIVED
);
169 mMessageType
->setChecked(mOptions
& FIND_MESSAGE
);
170 mFileType
->setChecked(mOptions
& FIND_FILE
);
171 mCommandType
->setChecked(mOptions
& FIND_COMMAND
);
172 mEmailType
->setChecked(mOptions
& FIND_EMAIL
);
173 mAudioType
->setChecked(mOptions
& FIND_AUDIO
);
175 connect(mDialog
.data(), &KFindDialog::okClicked
, this, &Find::slotFind
);
178 // Only display active/archived options if archived alarms are being kept
183 mActiveArchivedSep
->hide();
189 mActiveArchivedSep
->show();
192 // Disable options where no displayed alarms match them
194 bool archived
= false;
197 bool command
= false;
200 int rowCount
= mListView
->model()->rowCount();
201 for (int row
= 0; row
< rowCount
; ++row
)
203 KAEvent viewEvent
= mListView
->event(row
);
204 const KAEvent
* event
= &viewEvent
;
205 if (event
->expired())
209 switch (event
->actionTypes())
211 case KAEvent::ACT_EMAIL
: email
= true; break;
212 case KAEvent::ACT_AUDIO
: audio
= true; break;
213 case KAEvent::ACT_COMMAND
: command
= true; break;
214 case KAEvent::ACT_DISPLAY
:
215 if (event
->actionSubType() == KAEvent::FILE)
220 // fall through to ACT_DISPLAY_COMMAND
221 case KAEvent::ACT_DISPLAY_COMMAND
:
227 mLive
->setEnabled(live
);
228 mArchived
->setEnabled(archived
);
229 mMessageType
->setEnabled(text
);
230 mFileType
->setEnabled(file
);
231 mCommandType
->setEnabled(command
);
232 mEmailType
->setEnabled(email
);
233 mAudioType
->setEnabled(audio
);
235 mDialog
->setHasCursor(mListView
->selectionModel()->currentIndex().isValid());
239 /******************************************************************************
240 * Called when the user requests a search by clicking the dialog OK button.
242 void Find::slotFind()
246 mHistory
= mDialog
->findHistory(); // save search history so that it can be displayed again
247 mOptions
= mDialog
->options() & ~FIND_KALARM_OPTIONS
;
248 if ((mOptions
& KFind::RegularExpression
) && !QRegExp(mDialog
->pattern()).isValid())
250 mOptions
|= (mLive
->isEnabled() && mLive
->isChecked() ? FIND_LIVE
: 0)
251 | (mArchived
->isEnabled() && mArchived
->isChecked() ? FIND_ARCHIVED
: 0)
252 | (mMessageType
->isEnabled() && mMessageType
->isChecked() ? FIND_MESSAGE
: 0)
253 | (mFileType
->isEnabled() && mFileType
->isChecked() ? FIND_FILE
: 0)
254 | (mCommandType
->isEnabled() && mCommandType
->isChecked() ? FIND_COMMAND
: 0)
255 | (mEmailType
->isEnabled() && mEmailType
->isChecked() ? FIND_EMAIL
: 0)
256 | (mAudioType
->isEnabled() && mAudioType
->isChecked() ? FIND_AUDIO
: 0);
257 if (!(mOptions
& (FIND_LIVE
| FIND_ARCHIVED
))
258 || !(mOptions
& (FIND_MESSAGE
| FIND_FILE
| FIND_COMMAND
| FIND_EMAIL
| FIND_AUDIO
)))
260 KAMessageBox::sorry(mDialog
, i18nc("@info", "No alarm types are selected to search"));
264 // Supply KFind with only those options which relate to the text within alarms
265 long options
= mOptions
& (KFind::WholeWordsOnly
| KFind::CaseSensitive
| KFind::RegularExpression
);
266 bool newFind
= !mFind
;
267 bool newPattern
= (mDialog
->pattern() != mLastPattern
);
268 mLastPattern
= mDialog
->pattern();
271 mFind
->resetCounts();
272 mFind
->setPattern(mLastPattern
);
273 mFind
->setOptions(options
);
277 mFind
= new KFind(mLastPattern
, options
, mListView
, mDialog
);
278 connect(mFind
, &KFind::destroyed
, this, &Find::slotKFindDestroyed
);
279 mFind
->closeFindNextDialog(); // prevent 'Find Next' dialog appearing
282 // Set the starting point for the search
284 mNoCurrentItem
= newPattern
;
285 bool checkEnd
= false;
289 if (mOptions
& KFind::FromCursor
)
291 QModelIndex index
= mListView
->selectionModel()->currentIndex();
294 mStartID
= mListView
->event(index
).id();
295 mNoCurrentItem
= false;
301 // Execute the search
302 findNext(true, checkEnd
, false);
303 if (mFind
&& newFind
)
307 /******************************************************************************
308 * Perform the search.
309 * If 'fromCurrent' is true, the search starts with the current search item;
310 * otherwise, it starts from the next item.
312 void Find::findNext(bool forward
, bool checkEnd
, bool fromCurrent
)
316 index
= mListView
->selectionModel()->currentIndex();
318 index
= nextItem(index
, forward
);
320 // Search successive alarms until a match is found or the end is reached
323 for ( ; index
.isValid() && !last
; index
= nextItem(index
, forward
))
325 KAEvent viewEvent
= mListView
->event(index
);
326 const KAEvent
* event
= &viewEvent
;
327 if (!fromCurrent
&& !mStartID
.isNull() && mStartID
== event
->id())
328 last
= true; // we've wrapped round and reached the starting alarm again
330 bool live
= !event
->expired();
331 if ((live
&& !(mOptions
& FIND_LIVE
))
332 || (!live
&& !(mOptions
& FIND_ARCHIVED
)))
333 continue; // we're not searching this type of alarm
334 switch (event
->actionTypes())
336 case KAEvent::ACT_EMAIL
:
337 if (!(mOptions
& FIND_EMAIL
))
339 mFind
->setData(event
->emailAddresses(QStringLiteral(", ")));
340 found
= (mFind
->find() == KFind::Match
);
343 mFind
->setData(event
->emailSubject());
344 found
= (mFind
->find() == KFind::Match
);
347 mFind
->setData(event
->emailAttachments().join(QStringLiteral(", ")));
348 found
= (mFind
->find() == KFind::Match
);
351 mFind
->setData(event
->cleanText());
352 found
= (mFind
->find() == KFind::Match
);
355 case KAEvent::ACT_AUDIO
:
356 if (!(mOptions
& FIND_AUDIO
))
358 mFind
->setData(event
->audioFile());
359 found
= (mFind
->find() == KFind::Match
);
362 case KAEvent::ACT_COMMAND
:
363 if (!(mOptions
& FIND_COMMAND
))
365 mFind
->setData(event
->cleanText());
366 found
= (mFind
->find() == KFind::Match
);
369 case KAEvent::ACT_DISPLAY
:
370 if (event
->actionSubType() == KAEvent::FILE)
372 if (!(mOptions
& FIND_FILE
))
374 mFind
->setData(event
->cleanText());
375 found
= (mFind
->find() == KFind::Match
);
378 // fall through to ACT_DISPLAY_COMMAND
379 case KAEvent::ACT_DISPLAY_COMMAND
:
380 if (!(mOptions
& FIND_MESSAGE
))
382 mFind
->setData(event
->cleanText());
383 found
= (mFind
->find() == KFind::Match
);
392 // Process the search result
393 mNoCurrentItem
= !index
.isValid();
396 // A matching alarm was found - highlight it and make it current
398 QItemSelectionModel
* sel
= mListView
->selectionModel();
399 sel
->select(index
, QItemSelectionModel::ClearAndSelect
| QItemSelectionModel::Rows
);
400 sel
->setCurrentIndex(index
, QItemSelectionModel::ClearAndSelect
| QItemSelectionModel::Rows
);
401 mListView
->scrollTo(index
);
405 // No match was found
406 if (mFound
|| checkEnd
)
408 QString msg
= forward
? xi18nc("@info", "<para>End of alarm list reached.</para><para>Continue from the beginning?</para>")
409 : xi18nc("@info", "<para>Beginning of alarm list reached.</para><para>Continue from the end?</para>");
410 if (KAMessageBox::questionYesNo(mListView
, msg
, QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel()) == KMessageBox::Yes
)
412 mNoCurrentItem
= true;
413 findNext(forward
, false, false);
418 mFind
->displayFinalDialog(); // display "no match was found"
419 mNoCurrentItem
= false; // restart from the currently highlighted alarm if Find Next etc selected
423 /******************************************************************************
424 * Get the next alarm item to search.
426 QModelIndex
Find::nextItem(const QModelIndex
& index
, bool forward
) const
428 if (mOptions
& KFind::FindBackwards
)
430 if (!index
.isValid())
432 QAbstractItemModel
* model
= mListView
->model();
434 return model
->index(0, 0);
436 return model
->index(model
->rowCount() - 1, 0);
439 return mListView
->indexBelow(index
);
441 return mListView
->indexAbove(index
);