Warning, file /pim/kalarm/src/find.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * find.cpp - search facility 0003 * Program: kalarm 0004 * SPDX-FileCopyrightText: 2005-2023 David Jarvie <djarvie@kde.org> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "find.h" 0010 0011 #include "alarmlistview.h" 0012 #include "eventlistview.h" 0013 #include "preferences.h" 0014 #include "resources/eventmodel.h" 0015 #include "lib/messagebox.h" 0016 #include "kalarmcalendar/kaevent.h" 0017 #include "config-kalarm.h" 0018 0019 #include <KFindDialog> 0020 #include <KFind> 0021 #include <KSeparator> 0022 #include <KLocalizedString> 0023 #if ENABLE_X11 0024 #include <KX11Extras> 0025 #endif 0026 0027 #include <QGroupBox> 0028 #include <QCheckBox> 0029 #include <QVBoxLayout> 0030 #include <QGridLayout> 0031 #include <QRegularExpression> 0032 0033 using namespace KAlarmCal; 0034 0035 // KAlarm-specific options for Find dialog 0036 enum { 0037 FIND_LIVE = KFind::MinimumUserOption, 0038 FIND_ARCHIVED = KFind::MinimumUserOption << 1, 0039 FIND_MESSAGE = KFind::MinimumUserOption << 2, 0040 FIND_FILE = KFind::MinimumUserOption << 3, 0041 FIND_COMMAND = KFind::MinimumUserOption << 4, 0042 FIND_EMAIL = KFind::MinimumUserOption << 5, 0043 FIND_AUDIO = KFind::MinimumUserOption << 6 0044 }; 0045 static long FIND_KALARM_OPTIONS = FIND_LIVE | FIND_ARCHIVED | FIND_MESSAGE | FIND_FILE | FIND_COMMAND | FIND_EMAIL | FIND_AUDIO; 0046 0047 0048 Find::Find(EventListView* parent) 0049 : QObject(parent) 0050 , mListView(parent) 0051 , mDialog(nullptr) 0052 { 0053 connect(mListView->selectionModel(), &QItemSelectionModel::currentChanged, this, &Find::slotSelectionChanged); 0054 } 0055 0056 Find::~Find() 0057 { 0058 delete mDialog; // automatically set to null 0059 delete mFind; 0060 mFind = nullptr; 0061 } 0062 0063 void Find::slotSelectionChanged() 0064 { 0065 if (mDialog) 0066 mDialog->setHasCursor(mListView->selectionModel()->currentIndex().isValid()); 0067 } 0068 0069 /****************************************************************************** 0070 * Display the Find dialog. 0071 */ 0072 void Find::display() 0073 { 0074 if (!mOptions) 0075 { 0076 // Set defaults the first time the Find dialog is activated 0077 mOptions = FIND_LIVE | FIND_ARCHIVED | FIND_MESSAGE | FIND_FILE | FIND_COMMAND | FIND_EMAIL | FIND_AUDIO; 0078 } 0079 bool noArchived = !Preferences::archivedKeepDays(); 0080 bool showArchived = qobject_cast<AlarmListView*>(mListView) 0081 && (static_cast<AlarmListModel*>(mListView->model())->eventTypeFilter() & CalEvent::ARCHIVED); 0082 if (noArchived || !showArchived) // these settings could change between activations 0083 mOptions &= ~FIND_ARCHIVED; 0084 0085 if (mDialog) 0086 { 0087 #if ENABLE_X11 0088 KX11Extras::activateWindow(mDialog->winId()); 0089 #endif 0090 } 0091 else 0092 { 0093 mDialog = new KFindDialog(mListView, mOptions, mHistory, (mListView->selectionModel()->selectedRows().count() > 1)); 0094 mDialog->setModal(false); 0095 mDialog->setObjectName(QLatin1StringView("FindDlg")); 0096 mDialog->setHasSelection(false); 0097 QWidget* kalarmWidgets = mDialog->findExtension(); 0098 0099 // Alarm types 0100 auto layout = new QVBoxLayout(kalarmWidgets); 0101 layout->setContentsMargins(0, 0, 0, 0); 0102 QGroupBox* group = new QGroupBox(i18nc("@title:group", "Alarm Type"), kalarmWidgets); 0103 layout->addWidget(group); 0104 auto grid = new QGridLayout(group); 0105 grid->setColumnStretch(1, 1); 0106 0107 // Live & archived alarm selection 0108 mLive = new QCheckBox(i18nc("@option:check Alarm type", "Active"), group); 0109 mLive->setWhatsThis(i18nc("@info:whatsthis", "Check to include active alarms in the search.")); 0110 grid->addWidget(mLive, 1, 0, Qt::AlignLeft); 0111 0112 mArchived = new QCheckBox(i18nc("@option:check Alarm type", "Archived"), group); 0113 mArchived->setWhatsThis(i18nc("@info:whatsthis", "Check to include archived alarms in the search. " 0114 "This option is only available if archived alarms are currently being displayed.")); 0115 grid->addWidget(mArchived, 1, 2, Qt::AlignLeft); 0116 0117 mActiveArchivedSep = new KSeparator(Qt::Horizontal, kalarmWidgets); 0118 grid->addWidget(mActiveArchivedSep, 2, 0, 1, 3); 0119 0120 // Alarm actions 0121 mMessageType = new QCheckBox(i18nc("@option:check Alarm action = text display", "Text"), group); 0122 mMessageType->setWhatsThis(i18nc("@info:whatsthis", "Check to include text message alarms in the search.")); 0123 grid->addWidget(mMessageType, 3, 0); 0124 0125 mFileType = new QCheckBox(i18nc("@option:check Alarm action = file display", "File"), group); 0126 mFileType->setWhatsThis(i18nc("@info:whatsthis", "Check to include file alarms in the search.")); 0127 grid->addWidget(mFileType, 3, 2); 0128 0129 mCommandType = new QCheckBox(i18nc("@option:check Alarm action", "Command"), group); 0130 mCommandType->setWhatsThis(i18nc("@info:whatsthis", "Check to include command alarms in the search.")); 0131 grid->addWidget(mCommandType, 4, 0); 0132 0133 mEmailType = new QCheckBox(i18nc("@option:check Alarm action", "Email"), group); 0134 mEmailType->setWhatsThis(i18nc("@info:whatsthis", "Check to include email alarms in the search.")); 0135 grid->addWidget(mEmailType, 4, 2); 0136 0137 mAudioType = new QCheckBox(i18nc("@option:check Alarm action", "Audio"), group); 0138 mAudioType->setWhatsThis(i18nc("@info:whatsthis", "Check to include audio alarms in the search.")); 0139 grid->addWidget(mAudioType, 5, 0); 0140 0141 // Set defaults 0142 mLive->setChecked(mOptions & FIND_LIVE); 0143 mArchived->setChecked(mOptions & FIND_ARCHIVED); 0144 mMessageType->setChecked(mOptions & FIND_MESSAGE); 0145 mFileType->setChecked(mOptions & FIND_FILE); 0146 mCommandType->setChecked(mOptions & FIND_COMMAND); 0147 mEmailType->setChecked(mOptions & FIND_EMAIL); 0148 mAudioType->setChecked(mOptions & FIND_AUDIO); 0149 0150 connect(mDialog.data(), &KFindDialog::okClicked, this, &Find::slotFind); 0151 } 0152 0153 // Only display active/archived options if archived alarms are being kept 0154 if (noArchived) 0155 { 0156 mLive->hide(); 0157 mArchived->hide(); 0158 mActiveArchivedSep->hide(); 0159 } 0160 else 0161 { 0162 mLive->show(); 0163 mArchived->show(); 0164 mActiveArchivedSep->show(); 0165 } 0166 0167 // Disable options where no displayed alarms match them 0168 bool live = false; 0169 bool archived = false; 0170 bool text = false; 0171 bool file = false; 0172 bool command = false; 0173 bool email = false; 0174 bool audio = false; 0175 const int rowCount = mListView->model()->rowCount(); 0176 for (int row = 0; row < rowCount; ++row) 0177 { 0178 const KAEvent viewEvent = mListView->event(row); 0179 const KAEvent* event = &viewEvent; 0180 if (event->expired()) 0181 archived = true; 0182 else 0183 live = true; 0184 switch (event->actionTypes()) 0185 { 0186 case KAEvent::Action::Email: email = true; break; 0187 case KAEvent::Action::Audio: audio = true; break; 0188 case KAEvent::Action::Command: command = true; break; 0189 case KAEvent::Action::Display: 0190 if (event->actionSubType() == KAEvent::SubAction::File) 0191 { 0192 file = true; 0193 break; 0194 } 0195 // fall through to DisplayCommand 0196 [[fallthrough]]; 0197 case KAEvent::Action::DisplayCommand: 0198 default: 0199 text = true; 0200 break; 0201 } 0202 } 0203 mLive->setEnabled(live); 0204 mArchived->setEnabled(archived); 0205 mMessageType->setEnabled(text); 0206 mFileType->setEnabled(file); 0207 mCommandType->setEnabled(command); 0208 mEmailType->setEnabled(email); 0209 mAudioType->setEnabled(audio); 0210 0211 mDialog->setHasCursor(mListView->selectionModel()->currentIndex().isValid()); 0212 mDialog->show(); 0213 } 0214 0215 /****************************************************************************** 0216 * Called when the user requests a search by clicking the dialog OK button. 0217 */ 0218 void Find::slotFind() 0219 { 0220 if (!mDialog) 0221 return; 0222 mHistory = mDialog->findHistory(); // save search history so that it can be displayed again 0223 mOptions = mDialog->options() & ~FIND_KALARM_OPTIONS; 0224 if ((mOptions & KFind::RegularExpression) && !QRegularExpression(mDialog->pattern()).isValid()) 0225 return; 0226 mOptions |= (mLive->isEnabled() && mLive->isChecked() ? FIND_LIVE : 0) 0227 | (mArchived->isEnabled() && mArchived->isChecked() ? FIND_ARCHIVED : 0) 0228 | (mMessageType->isEnabled() && mMessageType->isChecked() ? FIND_MESSAGE : 0) 0229 | (mFileType->isEnabled() && mFileType->isChecked() ? FIND_FILE : 0) 0230 | (mCommandType->isEnabled() && mCommandType->isChecked() ? FIND_COMMAND : 0) 0231 | (mEmailType->isEnabled() && mEmailType->isChecked() ? FIND_EMAIL : 0) 0232 | (mAudioType->isEnabled() && mAudioType->isChecked() ? FIND_AUDIO : 0); 0233 if (!(mOptions & (FIND_LIVE | FIND_ARCHIVED)) 0234 || !(mOptions & (FIND_MESSAGE | FIND_FILE | FIND_COMMAND | FIND_EMAIL | FIND_AUDIO))) 0235 { 0236 KAMessageBox::error(mDialog, i18nc("@info", "No alarm types are selected to search")); 0237 return; 0238 } 0239 0240 // Supply KFind with only those options which relate to the text within alarms 0241 const long options = mOptions & (KFind::WholeWordsOnly | KFind::CaseSensitive | KFind::RegularExpression); 0242 const bool newFind = !mFind; 0243 const bool newPattern = (mDialog->pattern() != mLastPattern); 0244 mLastPattern = mDialog->pattern(); 0245 if (mFind) 0246 { 0247 mFind->resetCounts(); 0248 mFind->setPattern(mLastPattern); 0249 mFind->setOptions(options); 0250 } 0251 else 0252 { 0253 mFind = new KFind(mLastPattern, options, mListView, mDialog); 0254 connect(mFind, &KFind::destroyed, this, &Find::slotKFindDestroyed); 0255 mFind->closeFindNextDialog(); // prevent 'Find Next' dialog appearing 0256 } 0257 0258 // Set the starting point for the search 0259 mStartID.clear(); 0260 mNoCurrentItem = newPattern; 0261 bool checkEnd = false; 0262 if (newPattern) 0263 { 0264 mFound = false; 0265 if (mOptions & KFind::FromCursor) 0266 { 0267 const QModelIndex index = mListView->selectionModel()->currentIndex(); 0268 if (index.isValid()) 0269 { 0270 mStartID = mListView->event(index).id(); 0271 mNoCurrentItem = false; 0272 checkEnd = true; 0273 } 0274 } 0275 } 0276 0277 // Execute the search 0278 findNext(true, checkEnd, false); 0279 if (mFind && newFind) 0280 Q_EMIT active(true); 0281 } 0282 0283 /****************************************************************************** 0284 * Perform the search. 0285 * If 'fromCurrent' is true, the search starts with the current search item; 0286 * otherwise, it starts from the next item. 0287 */ 0288 void Find::findNext(bool forward, bool checkEnd, bool fromCurrent) 0289 { 0290 QModelIndex index; 0291 if (!mNoCurrentItem) 0292 index = mListView->selectionModel()->currentIndex(); 0293 if (!fromCurrent) 0294 index = nextItem(index, forward); 0295 0296 // Search successive alarms until a match is found or the end is reached 0297 bool found = false; 0298 bool last = false; 0299 for ( ; index.isValid() && !last; index = nextItem(index, forward)) 0300 { 0301 const KAEvent viewEvent = mListView->event(index); 0302 const KAEvent* event = &viewEvent; 0303 if (!fromCurrent && !mStartID.isNull() && mStartID == event->id()) 0304 last = true; // we've wrapped round and reached the starting alarm again 0305 fromCurrent = false; 0306 const bool live = !event->expired(); 0307 if ((live && !(mOptions & FIND_LIVE)) 0308 || (!live && !(mOptions & FIND_ARCHIVED))) 0309 continue; // we're not searching this type of alarm 0310 switch (event->actionTypes()) 0311 { 0312 case KAEvent::Action::Email: 0313 if (!(mOptions & FIND_EMAIL)) 0314 break; 0315 mFind->setData(event->emailAddresses(QStringLiteral(", "))); 0316 found = (mFind->find() == KFind::Match); 0317 if (found) 0318 break; 0319 mFind->setData(event->emailSubject()); 0320 found = (mFind->find() == KFind::Match); 0321 if (found) 0322 break; 0323 mFind->setData(event->emailAttachments().join(QLatin1String(", "))); 0324 found = (mFind->find() == KFind::Match); 0325 if (found) 0326 break; 0327 mFind->setData(event->cleanText()); 0328 found = (mFind->find() == KFind::Match); 0329 break; 0330 0331 case KAEvent::Action::Audio: 0332 if (!(mOptions & FIND_AUDIO)) 0333 break; 0334 mFind->setData(event->audioFile()); 0335 found = (mFind->find() == KFind::Match); 0336 break; 0337 0338 case KAEvent::Action::Command: 0339 if (!(mOptions & FIND_COMMAND)) 0340 break; 0341 mFind->setData(event->cleanText()); 0342 found = (mFind->find() == KFind::Match); 0343 break; 0344 0345 case KAEvent::Action::Display: 0346 if (event->actionSubType() == KAEvent::SubAction::File) 0347 { 0348 if (!(mOptions & FIND_FILE)) 0349 break; 0350 mFind->setData(event->cleanText()); 0351 found = (mFind->find() == KFind::Match); 0352 break; 0353 } 0354 // fall through to DisplayCommand 0355 [[fallthrough]]; 0356 case KAEvent::Action::DisplayCommand: 0357 if (!(mOptions & FIND_MESSAGE)) 0358 break; 0359 mFind->setData(event->cleanText()); 0360 found = (mFind->find() == KFind::Match); 0361 break; 0362 default: 0363 break; 0364 } 0365 if (found) 0366 break; 0367 } 0368 0369 // Process the search result 0370 mNoCurrentItem = !index.isValid(); 0371 if (found) 0372 { 0373 // A matching alarm was found - highlight it and make it current 0374 mFound = true; 0375 QItemSelectionModel* sel = mListView->selectionModel(); 0376 sel->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); 0377 sel->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); 0378 mListView->scrollTo(index); 0379 } 0380 else 0381 { 0382 // No match was found 0383 if (mFound || checkEnd) 0384 { 0385 const QString msg = forward ? xi18nc("@info", "<para>End of alarm list reached.</para><para>Continue from the beginning?</para>") 0386 : xi18nc("@info", "<para>Beginning of alarm list reached.</para><para>Continue from the end?</para>"); 0387 if (KAMessageBox::questionYesNo(mListView, msg, QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel()) == KMessageBox::ButtonCode::PrimaryAction) 0388 { 0389 mNoCurrentItem = true; 0390 findNext(forward, false, false); 0391 return; 0392 } 0393 } 0394 else 0395 mFind->displayFinalDialog(); // display "no match was found" 0396 mNoCurrentItem = false; // restart from the currently highlighted alarm if Find Next etc selected 0397 } 0398 } 0399 0400 /****************************************************************************** 0401 * Get the next alarm item to search. 0402 */ 0403 QModelIndex Find::nextItem(const QModelIndex& index, bool forward) const 0404 { 0405 if (mOptions & KFind::FindBackwards) 0406 forward = !forward; 0407 if (!index.isValid()) 0408 { 0409 QAbstractItemModel* model = mListView->model(); 0410 if (forward) 0411 return model->index(0, 0); 0412 else 0413 return model->index(model->rowCount() - 1, 0); 0414 } 0415 if (forward) 0416 return mListView->indexBelow(index); 0417 else 0418 return mListView->indexAbove(index); 0419 } 0420 0421 #include "moc_find.cpp" 0422 0423 // vim: et sw=4: