kalarm

find.cpp

00001 /*
00002  *  find.cpp  -  search facility 
00003  *  Program:  kalarm
00004  *  Copyright © 2005,2006 by David Jarvie <software@astrojar.org.uk>
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     if (mFind)
00232     {
00233         mFind->setPattern(mDialog->pattern());
00234         mFind->setOptions(options);
00235         delete mDialog;    // automatically set to 0
00236         findNext(true, true, false);
00237     }
00238     else
00239     {
00240 #ifdef MODAL_FIND
00241         mFind = new KFind(mDialog->pattern(), options, mListView);
00242 #else
00243         mFind = new KFind(mDialog->pattern(), options, mListView, mDialog);
00244 #endif
00245         connect(mFind, SIGNAL(destroyed()), SLOT(slotKFindDestroyed()));
00246         delete mDialog;                  // close the Find dialogue. Automatically set to 0.
00247         mFind->closeFindNextDialog();    // prevent 'Find Next' dialog appearing
00248 
00249         // Set the starting point for the search
00250         mListView->sort();     // ensure the whole list is sorted, not just the visible items
00251         mStartID       = QString::null;
00252         mNoCurrentItem = true;
00253         bool fromCurrent = false;
00254         EventListViewItemBase* item = 0;
00255         if (mOptions & KFindDialog::FromCursor)
00256         {
00257             item = mListView->currentItem();
00258             if (item)
00259             {
00260                 mStartID       = item->event().id();
00261                 mNoCurrentItem = false;
00262                 fromCurrent    = true;
00263             }
00264         }
00265 
00266         // Execute the search
00267         mFound = false;
00268         findNext(true, false, fromCurrent);
00269         if (mFind)
00270             emit active(true);
00271     }
00272 }
00273 
00274 /******************************************************************************
00275 *  Perform the search.
00276 *  If 'fromCurrent' is true, the search starts with the current search item;
00277 *  otherwise, it starts from the next item.
00278 */
00279 void Find::findNext(bool forward, bool sort, bool fromCurrent)
00280 {
00281     if (sort)
00282         mListView->sort();    // ensure the whole list is sorted, not just the visible items
00283 
00284     EventListViewItemBase* item = mNoCurrentItem ? 0 : mListView->currentItem();
00285     if (!fromCurrent)
00286         item = nextItem(item, forward);
00287 
00288     // Search successive alarms until a match is found or the end is reached
00289     bool found = false;
00290     for ( ;  item;  item = nextItem(item, forward))
00291     {
00292         const KAEvent& event = item->event();
00293         if (!fromCurrent  &&  !mStartID.isNull()  &&  mStartID == event.id())
00294             break;    // we've wrapped round and reached the starting alarm again
00295         fromCurrent = false;
00296         bool live = !event.expired();
00297         if (live  &&  !(mOptions & FIND_LIVE)
00298         ||  !live  &&  !(mOptions & FIND_EXPIRED))
00299             continue;     // we're not searching this type of alarm
00300         switch (event.action())
00301         {
00302             case KAEvent::MESSAGE:
00303                 if (!(mOptions & FIND_MESSAGE))
00304                     break;
00305                 mFind->setData(event.cleanText());
00306                 found = (mFind->find() == KFind::Match);
00307                 break;
00308 
00309             case KAEvent::FILE:
00310                 if (!(mOptions & FIND_FILE))
00311                     break;
00312                 mFind->setData(event.cleanText());
00313                 found = (mFind->find() == KFind::Match);
00314                 break;
00315 
00316             case KAEvent::COMMAND:
00317                 if (!(mOptions & FIND_COMMAND))
00318                     break;
00319                 mFind->setData(event.cleanText());
00320                 found = (mFind->find() == KFind::Match);
00321                 break;
00322 
00323             case KAEvent::EMAIL:
00324                 if (!(mOptions & FIND_EMAIL))
00325                     break;
00326                 mFind->setData(event.emailAddresses(", "));
00327                 found = (mFind->find() == KFind::Match);
00328                 if (found)
00329                     break;
00330                 mFind->setData(event.emailSubject());
00331                 found = (mFind->find() == KFind::Match);
00332                 if (found)
00333                     break;
00334                 mFind->setData(event.emailAttachments().join(", "));
00335                 found = (mFind->find() == KFind::Match);
00336                 if (found)
00337                     break;
00338                 mFind->setData(event.cleanText());
00339                 found = (mFind->find() == KFind::Match);
00340                 break;
00341         }
00342         if (found)
00343             break;
00344     }
00345 
00346     // Process the search result
00347     mNoCurrentItem = !item;
00348     if (found)
00349     {
00350         // A matching alarm was found - highlight it and make it current
00351         mFound = true;
00352         mListView->clearSelection();
00353         mListView->setSelected(item, true);
00354         mListView->setCurrentItem(item);
00355         mListView->ensureItemVisible(item);
00356     }
00357     else
00358     {
00359         // No match was found
00360         if (mFound)
00361         {
00362             QString msg = forward ? i18n("End of alarm list reached.\nContinue from the beginning?")
00363                                   : i18n("Beginning of alarm list reached.\nContinue from the end?");
00364             if (KMessageBox::questionYesNo(mListView, msg, QString::null, KStdGuiItem::cont(), KStdGuiItem::cancel()) == KMessageBox::Yes)
00365             {
00366                 findNext(forward, false);
00367                 return;
00368             }
00369         }
00370         else
00371             mFind->displayFinalDialog();     // display "no match was found"
00372         mNoCurrentItem = false;    // restart from the currently highlighted alarm if Find Next etc selected
00373     }
00374 }
00375 
00376 /******************************************************************************
00377 *  Get the next alarm item to search.
00378 */
00379 EventListViewItemBase* Find::nextItem(EventListViewItemBase* item, bool forward) const
00380 {
00381     QListViewItem* it;
00382     if (mOptions & KFindDialog::FindBackwards)
00383         forward = !forward;
00384     if (forward)
00385         it = item ? item->itemBelow() : mListView->firstChild();
00386     else
00387         it = item ? item->itemAbove() : mListView->lastItem();
00388     return (EventListViewItemBase*)it;
00389 }
KDE Home | KDE Accessibility Home | Description of Access Keys