kalarm

find.cpp
1 /*
2  * find.cpp - search facility
3  * Program: kalarm
4  * Copyright © 2005,2006,2008 by David Jarvie <djarvie@kde.org>
5  *
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.
10  *
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.
15  *
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.
19  */
20 
21 #include "kalarm.h"
22 
23 #include <tqlayout.h>
24 #include <tqwhatsthis.h>
25 #include <tqgroupbox.h>
26 #include <tqcheckbox.h>
27 
28 #include <kfinddialog.h>
29 #include <kfind.h>
30 #include <kseparator.h>
31 #include <twin.h>
32 #include <tdelocale.h>
33 #include <tdemessagebox.h>
34 #include <kdebug.h>
35 
36 #include "alarmlistview.h"
37 #include "preferences.h"
38 #include "find.moc"
39 
40 // KAlarm-specific options for Find dialog
41 enum {
42  FIND_LIVE = KFindDialog::MinimumUserOption,
43  FIND_EXPIRED = KFindDialog::MinimumUserOption << 1,
44  FIND_MESSAGE = KFindDialog::MinimumUserOption << 2,
45  FIND_FILE = KFindDialog::MinimumUserOption << 3,
46  FIND_COMMAND = KFindDialog::MinimumUserOption << 4,
47  FIND_EMAIL = KFindDialog::MinimumUserOption << 5
48 };
49 static long FIND_KALARM_OPTIONS = FIND_LIVE | FIND_EXPIRED | FIND_MESSAGE | FIND_FILE | FIND_COMMAND | FIND_EMAIL;
50 
51 
52 Find::Find(EventListViewBase* parent)
53  : TQObject(parent),
54  mListView(parent),
55  mDialog(0),
56  mFind(0),
57  mOptions(0)
58 {
59 }
60 
61 Find::~Find()
62 {
63  delete mDialog; // automatically set to 0
64  delete mFind;
65  mFind = 0;
66 }
67 
68 /******************************************************************************
69 * Display the Find dialog.
70 */
71 void Find::display()
72 {
73  if (!mOptions)
74  // Set defaults the first time the Find dialog is activated
75  mOptions = FIND_LIVE | FIND_EXPIRED | FIND_MESSAGE | FIND_FILE | FIND_COMMAND | FIND_EMAIL;
76  bool noExpired = !Preferences::expiredKeepDays();
77  bool showExpired = mListView->isA("AlarmListView") && ((AlarmListView*)mListView)->showingExpired();
78  if (noExpired || !showExpired) // these settings could change between activations
79  mOptions &= ~FIND_EXPIRED;
80 
81  if (mDialog)
82  {
83  KWin::activateWindow(mDialog->winId());
84  }
85  else
86  {
87 #ifdef MODAL_FIND
88  mDialog = new KFindDialog(mListView, "FindDlg", mOptions, mHistory, (mListView->selectedCount() > 1));
89 #else
90  mDialog = new KFindDialog(false, mListView, "FindDlg", mOptions, mHistory, (mListView->selectedCount() > 1));
91 #endif
92  mDialog->setHasSelection(false);
93  TQWidget* kalarmWidgets = mDialog->findExtension();
94 
95  // Alarm types
96  TQBoxLayout* layout = new TQVBoxLayout(kalarmWidgets, 0, KDialog::spacingHint());
97  TQGroupBox* group = new TQGroupBox(i18n("Alarm Type"), kalarmWidgets);
98  layout->addWidget(group);
99  TQGridLayout* grid = new TQGridLayout(group, 2, 2, KDialog::marginHint(), KDialog::spacingHint());
100  grid->addRowSpacing(0, mDialog->fontMetrics().lineSpacing()/2);
101  grid->setColStretch(1, 1);
102 
103  // Live & expired alarm selection
104  mLive = new TQCheckBox(i18n("Acti&ve"), group);
105  mLive->setFixedSize(mLive->sizeHint());
106  TQWhatsThis::add(mLive, i18n("Check to include active alarms in the search."));
107  grid->addWidget(mLive, 1, 0, TQt::AlignAuto);
108 
109  mExpired = new TQCheckBox(i18n("Ex&pired"), group);
110  mExpired->setFixedSize(mExpired->sizeHint());
111  TQWhatsThis::add(mExpired,
112  i18n("Check to include expired alarms in the search. "
113  "This option is only available if expired alarms are currently being displayed."));
114  grid->addWidget(mExpired, 1, 2, TQt::AlignAuto);
115 
116  mActiveExpiredSep = new KSeparator(TQt::Horizontal, kalarmWidgets);
117  grid->addMultiCellWidget(mActiveExpiredSep, 2, 2, 0, 2);
118 
119  // Alarm actions
120  mMessageType = new TQCheckBox(i18n("Text"), group, "message");
121  mMessageType->setFixedSize(mMessageType->sizeHint());
122  TQWhatsThis::add(mMessageType, i18n("Check to include text message alarms in the search."));
123  grid->addWidget(mMessageType, 3, 0);
124 
125  mFileType = new TQCheckBox(i18n("Fi&le"), group, "file");
126  mFileType->setFixedSize(mFileType->sizeHint());
127  TQWhatsThis::add(mFileType, i18n("Check to include file alarms in the search."));
128  grid->addWidget(mFileType, 3, 2);
129 
130  mCommandType = new TQCheckBox(i18n("Co&mmand"), group, "command");
131  mCommandType->setFixedSize(mCommandType->sizeHint());
132  TQWhatsThis::add(mCommandType, i18n("Check to include command alarms in the search."));
133  grid->addWidget(mCommandType, 4, 0);
134 
135  mEmailType = new TQCheckBox(i18n("&Email"), group, "email");
136  mEmailType->setFixedSize(mEmailType->sizeHint());
137  TQWhatsThis::add(mEmailType, i18n("Check to include email alarms in the search."));
138  grid->addWidget(mEmailType, 4, 2);
139 
140  // Set defaults
141  mLive->setChecked(mOptions & FIND_LIVE);
142  mExpired->setChecked(mOptions & FIND_EXPIRED);
143  mMessageType->setChecked(mOptions & FIND_MESSAGE);
144  mFileType->setChecked(mOptions & FIND_FILE);
145  mCommandType->setChecked(mOptions & FIND_COMMAND);
146  mEmailType->setChecked(mOptions & FIND_EMAIL);
147 
148 #ifndef MODAL_FIND
149  connect(mDialog, TQ_SIGNAL(okClicked()), this, TQ_SLOT(slotFind()));
150 #endif
151  }
152 
153  // Only display active/expired options if expired alarms are being kept
154  if (noExpired)
155  {
156  mLive->hide();
157  mExpired->hide();
158  mActiveExpiredSep->hide();
159  }
160  else
161  {
162  mLive->show();
163  mExpired->show();
164  mActiveExpiredSep->show();
165  }
166 
167  // Disable options where no displayed alarms match them
168  bool live = false;
169  bool expired = false;
170  bool text = false;
171  bool file = false;
172  bool command = false;
173  bool email = false;
174  for (EventListViewItemBase* item = mListView->firstChild(); item; item = item->nextSibling())
175  {
176  const KAEvent& event = item->event();
177  if (event.expired())
178  expired = true;
179  else
180  live = true;
181  switch (event.action())
182  {
183  case KAEvent::MESSAGE: text = true; break;
184  case KAEvent::FILE: file = true; break;
185  case KAEvent::COMMAND: command = true; break;
186  case KAEvent::EMAIL: email = true; break;
187  }
188  }
189  mLive->setEnabled(live);
190  mExpired->setEnabled(expired);
191  mMessageType->setEnabled(text);
192  mFileType->setEnabled(file);
193  mCommandType->setEnabled(command);
194  mEmailType->setEnabled(email);
195 
196  mDialog->setHasCursor(mListView->currentItem());
197 #ifdef MODAL_FIND
198  if (mDialog->exec() == TQDialog::Accepted)
199  slotFind();
200  else
201  delete mDialog;
202 #else
203  mDialog->show();
204 #endif
205 }
206 
207 /******************************************************************************
208 * Called when the user requests a search by clicking the dialog OK button.
209 */
210 void Find::slotFind()
211 {
212  if (!mDialog)
213  return;
214  mHistory = mDialog->findHistory(); // save search history so that it can be displayed again
215  mOptions = mDialog->options() & ~FIND_KALARM_OPTIONS;
216  mOptions |= (mLive->isEnabled() && mLive->isChecked() ? FIND_LIVE : 0)
217  | (mExpired->isEnabled() && mExpired->isChecked() ? FIND_EXPIRED : 0)
218  | (mMessageType->isEnabled() && mMessageType->isChecked() ? FIND_MESSAGE : 0)
219  | (mFileType->isEnabled() && mFileType->isChecked() ? FIND_FILE : 0)
220  | (mCommandType->isEnabled() && mCommandType->isChecked() ? FIND_COMMAND : 0)
221  | (mEmailType->isEnabled() && mEmailType->isChecked() ? FIND_EMAIL : 0);
222  if (!(mOptions & (FIND_LIVE | FIND_EXPIRED))
223  || !(mOptions & (FIND_MESSAGE | FIND_FILE | FIND_COMMAND | FIND_EMAIL)))
224  {
225  KMessageBox::sorry(mDialog, i18n("No alarm types are selected to search"));
226  return;
227  }
228 
229  // Supply KFind with only those options which relate to the text within alarms
230  long options = mOptions & (KFindDialog::WholeWordsOnly | KFindDialog::CaseSensitive | KFindDialog::RegularExpression);
231  bool newFind = !mFind;
232  bool newPattern = (mDialog->pattern() != mLastPattern);
233  mLastPattern = mDialog->pattern();
234  if (mFind)
235  {
236  mFind->resetCounts();
237  mFind->setPattern(mLastPattern);
238  mFind->setOptions(options);
239  }
240  else
241  {
242 #ifdef MODAL_FIND
243  mFind = new KFind(mLastPattern, options, mListView);
244  mDialog->deleteLater(); // automatically set to 0
245 #else
246  mFind = new KFind(mLastPattern, options, mListView, mDialog);
247 #endif
248  connect(mFind, TQ_SIGNAL(destroyed()), TQ_SLOT(slotKFindDestroyed()));
249  mFind->closeFindNextDialog(); // prevent 'Find Next' dialog appearing
250  }
251 
252  // Set the starting point for the search
253  mStartID = TQString();
254  mNoCurrentItem = newPattern;
255  bool checkEnd = false;
256  if (newPattern)
257  {
258  mFound = false;
259  if (mOptions & KFindDialog::FromCursor)
260  {
261  EventListViewItemBase* item = mListView->currentItem();
262  if (item)
263  {
264  mStartID = item->event().id();
265  mNoCurrentItem = false;
266  checkEnd = true;
267  }
268  }
269  }
270 
271  // Execute the search
272  findNext(true, true, checkEnd, false);
273  if (mFind && newFind)
274  emit active(true);
275 }
276 
277 /******************************************************************************
278 * Perform the search.
279 * If 'fromCurrent' is true, the search starts with the current search item;
280 * otherwise, it starts from the next item.
281 */
282 void Find::findNext(bool forward, bool sort, bool checkEnd, bool fromCurrent)
283 {
284  if (sort)
285  mListView->sort(); // ensure the whole list is sorted, not just the visible items
286 
287  EventListViewItemBase* item = mNoCurrentItem ? 0 : mListView->currentItem();
288  if (!fromCurrent)
289  item = nextItem(item, forward);
290 
291  // Search successive alarms until a match is found or the end is reached
292  bool found = false;
293  bool last = false;
294  for ( ; item && !last; item = nextItem(item, forward))
295  {
296  const KAEvent& event = item->event();
297  if (!fromCurrent && !mStartID.isNull() && mStartID == event.id())
298  last = true; // we've wrapped round and reached the starting alarm again
299  fromCurrent = false;
300  bool live = !event.expired();
301  if ((live && !(mOptions & FIND_LIVE))
302  || (!live && !(mOptions & FIND_EXPIRED)))
303  continue; // we're not searching this type of alarm
304  switch (event.action())
305  {
306  case KAEvent::MESSAGE:
307  if (!(mOptions & FIND_MESSAGE))
308  break;
309  mFind->setData(event.cleanText());
310  found = (mFind->find() == KFind::Match);
311  break;
312 
313  case KAEvent::FILE:
314  if (!(mOptions & FIND_FILE))
315  break;
316  mFind->setData(event.cleanText());
317  found = (mFind->find() == KFind::Match);
318  break;
319 
320  case KAEvent::COMMAND:
321  if (!(mOptions & FIND_COMMAND))
322  break;
323  mFind->setData(event.cleanText());
324  found = (mFind->find() == KFind::Match);
325  break;
326 
327  case KAEvent::EMAIL:
328  if (!(mOptions & FIND_EMAIL))
329  break;
330  mFind->setData(event.emailAddresses(", "));
331  found = (mFind->find() == KFind::Match);
332  if (found)
333  break;
334  mFind->setData(event.emailSubject());
335  found = (mFind->find() == KFind::Match);
336  if (found)
337  break;
338  mFind->setData(event.emailAttachments().join(", "));
339  found = (mFind->find() == KFind::Match);
340  if (found)
341  break;
342  mFind->setData(event.cleanText());
343  found = (mFind->find() == KFind::Match);
344  break;
345  }
346  if (found)
347  break;
348  }
349 
350  // Process the search result
351  mNoCurrentItem = !item;
352  if (found)
353  {
354  // A matching alarm was found - highlight it and make it current
355  mFound = true;
356  mListView->clearSelection();
357  mListView->setSelected(item, true);
358  mListView->setCurrentItem(item);
359  mListView->ensureItemVisible(item);
360  }
361  else
362  {
363  // No match was found
364  if (mFound || checkEnd)
365  {
366  TQString msg = forward ? i18n("End of alarm list reached.\nContinue from the beginning?")
367  : i18n("Beginning of alarm list reached.\nContinue from the end?");
368  if (KMessageBox::questionYesNo(mListView, msg, TQString(), KStdGuiItem::cont(), KStdGuiItem::cancel()) == KMessageBox::Yes)
369  {
370  mNoCurrentItem = true;
371  findNext(forward, false);
372  return;
373  }
374  }
375  else
376  mFind->displayFinalDialog(); // display "no match was found"
377  mNoCurrentItem = false; // restart from the currently highlighted alarm if Find Next etc selected
378  }
379 }
380 
381 /******************************************************************************
382 * Get the next alarm item to search.
383 */
384 EventListViewItemBase* Find::nextItem(EventListViewItemBase* item, bool forward) const
385 {
386  TQListViewItem* it;
387  if (mOptions & KFindDialog::FindBackwards)
388  forward = !forward;
389  if (forward)
390  it = item ? item->itemBelow() : mListView->firstChild();
391  else
392  it = item ? item->itemAbove() : mListView->lastItem();
393  return (EventListViewItemBase*)it;
394 }
KAEvent corresponds to a KCal::Event instance.
Definition: alarmevent.h:232