kalarm

find.cpp

00001 /*
00002  *  find.cpp  -  search facility 
00003  *  Program:  kalarm
00004  *  Copyright © 2005,2006,2008 by David Jarvie <djarvie@kde.org>
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License along
00017  *  with this program; if not, write to the Free Software Foundation, Inc.,
00018  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  */
00020 
00021 #include "kalarm.h"
00022 
00023 #include <qlayout.h>
00024 #include <qwhatsthis.h>
00025 #include <qgroupbox.h>
00026 #include <qcheckbox.h>
00027 
00028 #include <kfinddialog.h>
00029 #include <kfind.h>
00030 #include <kseparator.h>
00031 #include <kwin.h>
00032 #include <klocale.h>
00033 #include <kmessagebox.h>
00034 #include <kdebug.h>
00035 
00036 #include "alarmlistview.h"
00037 #include "preferences.h"
00038 #include "find.moc"
00039 
00040 // KAlarm-specific options for Find dialog
00041 enum {
00042     FIND_LIVE    = KFindDialog::MinimumUserOption,
00043     FIND_EXPIRED = KFindDialog::MinimumUserOption << 1,
00044     FIND_MESSAGE = KFindDialog::MinimumUserOption << 2,
00045     FIND_FILE    = KFindDialog::MinimumUserOption << 3,
00046     FIND_COMMAND = KFindDialog::MinimumUserOption << 4,
00047     FIND_EMAIL   = KFindDialog::MinimumUserOption << 5
00048 };
00049 static long FIND_KALARM_OPTIONS = FIND_LIVE | FIND_EXPIRED | FIND_MESSAGE | FIND_FILE | FIND_COMMAND | FIND_EMAIL;
00050 
00051 
00052 Find::Find(EventListViewBase* parent)
00053     : QObject(parent),
00054       mListView(parent),
00055       mDialog(0),
00056       mFind(0),
00057       mOptions(0)
00058 {
00059 }
00060 
00061 Find::~Find()
00062 {
00063     delete mDialog;    // automatically set to 0
00064     delete mFind;
00065     mFind = 0;
00066 }
00067 
00068 /******************************************************************************
00069 *  Display the Find dialog.
00070 */
00071 void Find::display()
00072 {
00073     if (!mOptions)
00074         // Set defaults the first time the Find dialog is activated
00075         mOptions = FIND_LIVE | FIND_EXPIRED | FIND_MESSAGE | FIND_FILE | FIND_COMMAND | FIND_EMAIL;
00076     bool noExpired = !Preferences::expiredKeepDays();
00077     bool showExpired = mListView->isA("AlarmListView") && ((AlarmListView*)mListView)->showingExpired();
00078     if (noExpired  ||  !showExpired)      // these settings could change between activations
00079         mOptions &= ~FIND_EXPIRED;
00080 
00081     if (mDialog)
00082     {
00083         KWin::activateWindow(mDialog->winId());
00084     }
00085     else
00086     {
00087 #ifdef MODAL_FIND
00088         mDialog = new KFindDialog(mListView, "FindDlg", mOptions, mHistory, (mListView->selectedCount() > 1));
00089 #else
00090         mDialog = new KFindDialog(false, mListView, "FindDlg", mOptions, mHistory, (mListView->selectedCount() > 1));
00091 #endif
00092         mDialog->setHasSelection(false);
00093         QWidget* kalarmWidgets = mDialog->findExtension();
00094 
00095         // Alarm types
00096         QBoxLayout* layout = new QVBoxLayout(kalarmWidgets, 0, KDialog::spacingHint());
00097         QGroupBox* group = new QGroupBox(i18n("Alarm Type"), kalarmWidgets);
00098         layout->addWidget(group);
00099         QGridLayout* grid = new QGridLayout(group, 2, 2, KDialog::marginHint(), KDialog::spacingHint());
00100         grid->addRowSpacing(0, mDialog->fontMetrics().lineSpacing()/2);
00101         grid->setColStretch(1, 1);
00102 
00103         // Live & expired alarm selection
00104         mLive = new QCheckBox(i18n("Acti&ve"), group);
00105         mLive->setFixedSize(mLive->sizeHint());
00106         QWhatsThis::add(mLive, i18n("Check to include active alarms in the search."));
00107         grid->addWidget(mLive, 1, 0, Qt::AlignAuto);
00108 
00109         mExpired = new QCheckBox(i18n("Ex&pired"), group);
00110         mExpired->setFixedSize(mExpired->sizeHint());
00111         QWhatsThis::add(mExpired,
00112               i18n("Check to include expired alarms in the search. "
00113                    "This option is only available if expired alarms are currently being displayed."));
00114         grid->addWidget(mExpired, 1, 2, Qt::AlignAuto);
00115 
00116         mActiveExpiredSep = new KSeparator(Qt::Horizontal, kalarmWidgets);
00117         grid->addMultiCellWidget(mActiveExpiredSep, 2, 2, 0, 2);
00118 
00119         // Alarm actions
00120         mMessageType = new QCheckBox(i18n("Text"), group, "message");
00121         mMessageType->setFixedSize(mMessageType->sizeHint());
00122         QWhatsThis::add(mMessageType, i18n("Check to include text message alarms in the search."));
00123         grid->addWidget(mMessageType, 3, 0);
00124 
00125         mFileType = new QCheckBox(i18n("Fi&le"), group, "file");
00126         mFileType->setFixedSize(mFileType->sizeHint());
00127         QWhatsThis::add(mFileType, i18n("Check to include file alarms in the search."));
00128         grid->addWidget(mFileType, 3, 2);
00129 
00130         mCommandType = new QCheckBox(i18n("Co&mmand"), group, "command");
00131         mCommandType->setFixedSize(mCommandType->sizeHint());
00132         QWhatsThis::add(mCommandType, i18n("Check to include command alarms in the search."));
00133         grid->addWidget(mCommandType, 4, 0);
00134 
00135         mEmailType = new QCheckBox(i18n("&Email"), group, "email");
00136         mEmailType->setFixedSize(mEmailType->sizeHint());
00137         QWhatsThis::add(mEmailType, i18n("Check to include email alarms in the search."));
00138         grid->addWidget(mEmailType, 4, 2);
00139 
00140         // Set defaults
00141         mLive->setChecked(mOptions & FIND_LIVE);
00142         mExpired->setChecked(mOptions & FIND_EXPIRED);
00143         mMessageType->setChecked(mOptions & FIND_MESSAGE);
00144         mFileType->setChecked(mOptions & FIND_FILE);
00145         mCommandType->setChecked(mOptions & FIND_COMMAND);
00146         mEmailType->setChecked(mOptions & FIND_EMAIL);
00147 
00148 #ifndef MODAL_FIND
00149         connect(mDialog, SIGNAL(okClicked()), this, SLOT(slotFind()));
00150 #endif
00151     }
00152 
00153     // Only display active/expired options if expired alarms are being kept
00154     if (noExpired)
00155     {
00156         mLive->hide();
00157         mExpired->hide();
00158         mActiveExpiredSep->hide();
00159     }
00160     else
00161     {
00162         mLive->show();
00163         mExpired->show();
00164         mActiveExpiredSep->show();
00165     }
00166 
00167     // Disable options where no displayed alarms match them
00168     bool live    = false;
00169     bool expired = false;
00170     bool text    = false;
00171     bool file    = false;
00172     bool command = false;
00173     bool email   = false;
00174     for (EventListViewItemBase* item = mListView->firstChild();  item;  item = item->nextSibling())
00175     {
00176         const KAEvent& event = item->event();
00177         if (event.expired())
00178             expired = true;
00179         else
00180             live = true;
00181         switch (event.action())
00182         {
00183             case KAEvent::MESSAGE:  text    = true;  break;
00184             case KAEvent::FILE:     file    = true;  break;
00185             case KAEvent::COMMAND:  command = true;  break;
00186             case KAEvent::EMAIL:    email   = true;  break;
00187         }
00188     }
00189     mLive->setEnabled(live);
00190     mExpired->setEnabled(expired);
00191     mMessageType->setEnabled(text);
00192     mFileType->setEnabled(file);
00193     mCommandType->setEnabled(command);
00194     mEmailType->setEnabled(email);
00195 
00196     mDialog->setHasCursor(mListView->currentItem());
00197 #ifdef MODAL_FIND
00198     if (mDialog->exec() == QDialog::Accepted)
00199         slotFind();
00200     else
00201         delete mDialog;
00202 #else
00203     mDialog->show();
00204 #endif
00205 }
00206 
00207 /******************************************************************************
00208 *  Called when the user requests a search by clicking the dialog OK button.
00209 */
00210 void Find::slotFind()
00211 {
00212     if (!mDialog)
00213         return;
00214     mHistory = mDialog->findHistory();    // save search history so that it can be displayed again
00215     mOptions = mDialog->options() & ~FIND_KALARM_OPTIONS;
00216     mOptions |= (mLive->isEnabled()        && mLive->isChecked()        ? FIND_LIVE : 0)
00217              |  (mExpired->isEnabled()     && mExpired->isChecked()     ? FIND_EXPIRED : 0)
00218              |  (mMessageType->isEnabled() && mMessageType->isChecked() ? FIND_MESSAGE : 0)
00219              |  (mFileType->isEnabled()    && mFileType->isChecked()    ? FIND_FILE : 0)
00220              |  (mCommandType->isEnabled() && mCommandType->isChecked() ? FIND_COMMAND : 0)
00221              |  (mEmailType->isEnabled()   && mEmailType->isChecked()   ? FIND_EMAIL : 0);
00222     if (!(mOptions & (FIND_LIVE | FIND_EXPIRED))
00223     ||  !(mOptions & (FIND_MESSAGE | FIND_FILE | FIND_COMMAND | FIND_EMAIL)))
00224     {
00225         KMessageBox::sorry(mDialog, i18n("No alarm types are selected to search"));
00226         return;
00227     }
00228 
00229     // Supply KFind with only those options which relate to the text within alarms
00230     long options = mOptions & (KFindDialog::WholeWordsOnly | KFindDialog::CaseSensitive | KFindDialog::RegularExpression);
00231     bool newFind = !mFind;
00232     bool newPattern = (mDialog->pattern() != mLastPattern);
00233     mLastPattern = mDialog->pattern();
00234     if (mFind)
00235     {
00236         mFind->resetCounts();
00237         mFind->setPattern(mLastPattern);
00238         mFind->setOptions(options);
00239     }
00240     else
00241     {
00242 #ifdef MODAL_FIND
00243         mFind = new KFind(mLastPattern, options, mListView);
00244         mDialog->deleteLater();    // automatically set to 0
00245 #else
00246         mFind = new KFind(mLastPattern, options, mListView, mDialog);
00247 #endif
00248         connect(mFind, SIGNAL(destroyed()), SLOT(slotKFindDestroyed()));
00249         mFind->closeFindNextDialog();    // prevent 'Find Next' dialog appearing
00250     }
00251 
00252     // Set the starting point for the search
00253     mStartID       = QString::null;
00254     mNoCurrentItem = newPattern;
00255     bool checkEnd = false;
00256     if (newPattern)
00257     {
00258         mFound = false;
00259         if (mOptions & KFindDialog::FromCursor)
00260         {
00261             EventListViewItemBase* item = mListView->currentItem();
00262             if (item)
00263             {
00264                 mStartID       = item->event().id();
00265                 mNoCurrentItem = false;
00266                 checkEnd = true;
00267             }
00268         }
00269     }
00270 
00271     // Execute the search
00272     findNext(true, true, checkEnd, false);
00273     if (mFind  &&  newFind)
00274         emit active(true);
00275 }
00276 
00277 /******************************************************************************
00278 *  Perform the search.
00279 *  If 'fromCurrent' is true, the search starts with the current search item;
00280 *  otherwise, it starts from the next item.
00281 */
00282 void Find::findNext(bool forward, bool sort, bool checkEnd, bool fromCurrent)
00283 {
00284     if (sort)
00285         mListView->sort();    // ensure the whole list is sorted, not just the visible items
00286 
00287     EventListViewItemBase* item = mNoCurrentItem ? 0 : mListView->currentItem();
00288     if (!fromCurrent)
00289         item = nextItem(item, forward);
00290 
00291     // Search successive alarms until a match is found or the end is reached
00292     bool found = false;
00293     bool last = false;
00294     for ( ;  item && !last;  item = nextItem(item, forward))
00295     {
00296         const KAEvent& event = item->event();
00297         if (!fromCurrent  &&  !mStartID.isNull()  &&  mStartID == event.id())
00298             last = true;    // we've wrapped round and reached the starting alarm again
00299         fromCurrent = false;
00300         bool live = !event.expired();
00301         if (live  &&  !(mOptions & FIND_LIVE)
00302         ||  !live  &&  !(mOptions & FIND_EXPIRED))
00303             continue;     // we're not searching this type of alarm
00304         switch (event.action())
00305         {
00306             case KAEvent::MESSAGE:
00307                 if (!(mOptions & FIND_MESSAGE))
00308                     break;
00309                 mFind->setData(event.cleanText());
00310                 found = (mFind->find() == KFind::Match);
00311                 break;
00312 
00313             case KAEvent::FILE:
00314                 if (!(mOptions & FIND_FILE))
00315                     break;
00316                 mFind->setData(event.cleanText());
00317                 found = (mFind->find() == KFind::Match);
00318                 break;
00319 
00320             case KAEvent::COMMAND:
00321                 if (!(mOptions & FIND_COMMAND))
00322                     break;
00323                 mFind->setData(event.cleanText());
00324                 found = (mFind->find() == KFind::Match);
00325                 break;
00326 
00327             case KAEvent::EMAIL:
00328                 if (!(mOptions & FIND_EMAIL))
00329                     break;
00330                 mFind->setData(event.emailAddresses(", "));
00331                 found = (mFind->find() == KFind::Match);
00332                 if (found)
00333                     break;
00334                 mFind->setData(event.emailSubject());
00335                 found = (mFind->find() == KFind::Match);
00336                 if (found)
00337                     break;
00338                 mFind->setData(event.emailAttachments().join(", "));
00339                 found = (mFind->find() == KFind::Match);
00340                 if (found)
00341                     break;
00342                 mFind->setData(event.cleanText());
00343                 found = (mFind->find() == KFind::Match);
00344                 break;
00345         }
00346         if (found)
00347             break;
00348     }
00349 
00350     // Process the search result
00351     mNoCurrentItem = !item;
00352     if (found)
00353     {
00354         // A matching alarm was found - highlight it and make it current
00355         mFound = true;
00356         mListView->clearSelection();
00357         mListView->setSelected(item, true);
00358         mListView->setCurrentItem(item);
00359         mListView->ensureItemVisible(item);
00360     }
00361     else
00362     {
00363         // No match was found
00364         if (mFound  ||  checkEnd)
00365         {
00366             QString msg = forward ? i18n("End of alarm list reached.\nContinue from the beginning?")
00367                                   : i18n("Beginning of alarm list reached.\nContinue from the end?");
00368             if (KMessageBox::questionYesNo(mListView, msg, QString::null, KStdGuiItem::cont(), KStdGuiItem::cancel()) == KMessageBox::Yes)
00369             {
00370                 mNoCurrentItem = true;
00371                 findNext(forward, false);
00372                 return;
00373             }
00374         }
00375         else
00376             mFind->displayFinalDialog();     // display "no match was found"
00377         mNoCurrentItem = false;    // restart from the currently highlighted alarm if Find Next etc selected
00378     }
00379 }
00380 
00381 /******************************************************************************
00382 *  Get the next alarm item to search.
00383 */
00384 EventListViewItemBase* Find::nextItem(EventListViewItemBase* item, bool forward) const
00385 {
00386     QListViewItem* it;
00387     if (mOptions & KFindDialog::FindBackwards)
00388         forward = !forward;
00389     if (forward)
00390         it = item ? item->itemBelow() : mListView->firstChild();
00391     else
00392         it = item ? item->itemAbove() : mListView->lastItem();
00393     return (EventListViewItemBase*)it;
00394 }
KDE Home | KDE Accessibility Home | Description of Access Keys