kalarm

birthdaydlg.cpp
00001 /*
00002  *  birthdaydlg.cpp  -  dialog to pick birthdays from address book
00003  *  Program:  kalarm
00004  *  Copyright © 2002-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 <tqlayout.h>
00024 #include <tqgroupbox.h>
00025 #include <tqhbox.h>
00026 #include <tqlabel.h>
00027 #include <tqlineedit.h>
00028 #include <tqwhatsthis.h>
00029 
00030 #include <klocale.h>
00031 #include <kglobal.h>
00032 #include <kconfig.h>
00033 #include <kmessagebox.h>
00034 #include <kaccel.h>
00035 #include <kabc/addressbook.h>
00036 #include <kabc/stdaddressbook.h>
00037 #include <kdebug.h>
00038 
00039 #include "alarmcalendar.h"
00040 #include "checkbox.h"
00041 #include "colourcombo.h"
00042 #include "editdlg.h"
00043 #include "fontcolourbutton.h"
00044 #include "kalarmapp.h"
00045 #include "latecancel.h"
00046 #include "preferences.h"
00047 #include "reminder.h"
00048 #include "repetition.h"
00049 #include "shellprocess.h"
00050 #include "soundpicker.h"
00051 #include "specialactions.h"
00052 #include "birthdaydlg.moc"
00053 
00054 using namespace KCal;
00055 
00056 
00057 class AddresseeItem : public TQListViewItem
00058 {
00059     public:
00060         enum columns { NAME = 0, BIRTHDAY = 1 };
00061         AddresseeItem(TQListView* parent, const TQString& name, const TQDate& birthday);
00062         TQDate birthday() const   { return mBirthday; }
00063         virtual TQString key(int column, bool ascending) const;
00064     private:
00065         TQDate     mBirthday;
00066         TQString   mBirthdayOrder;
00067 };
00068 
00069 
00070 const KABC::AddressBook* BirthdayDlg::mAddressBook = 0;
00071 
00072 
00073 BirthdayDlg::BirthdayDlg(TQWidget* parent)
00074     : KDialogBase(KDialogBase::Plain, i18n("Import Birthdays From KAddressBook"), Ok|Cancel, Ok, parent, "BirthdayDlg"),
00075       mSpecialActionsButton(0)
00076 {
00077     TQWidget* topWidget = plainPage();
00078     TQBoxLayout* topLayout = new TQVBoxLayout(topWidget);
00079     topLayout->setSpacing(spacingHint());
00080 
00081     // Prefix and suffix to the name in the alarm text
00082     // Get default prefix and suffix texts from config file
00083     KConfig* config = kapp->config();
00084     config->setGroup(TQString::fromLatin1("General"));
00085     mPrefixText = config->readEntry(TQString::fromLatin1("BirthdayPrefix"), i18n("Birthday: "));
00086     mSuffixText = config->readEntry(TQString::fromLatin1("BirthdaySuffix"));
00087 
00088     TQGroupBox* textGroup = new TQGroupBox(2, Qt::Horizontal, i18n("Alarm Text"), topWidget);
00089     topLayout->addWidget(textGroup);
00090     TQLabel* label = new TQLabel(i18n("Pre&fix:"), textGroup);
00091     mPrefix = new BLineEdit(mPrefixText, textGroup);
00092     mPrefix->setMinimumSize(mPrefix->sizeHint());
00093     label->setBuddy(mPrefix);
00094     connect(mPrefix, TQT_SIGNAL(focusLost()), TQT_SLOT(slotTextLostFocus()));
00095     TQWhatsThis::add(mPrefix,
00096           i18n("Enter text to appear before the person's name in the alarm message, "
00097                "including any necessary trailing spaces."));
00098 
00099     label = new TQLabel(i18n("S&uffix:"), textGroup);
00100     mSuffix = new BLineEdit(mSuffixText, textGroup);
00101     mSuffix->setMinimumSize(mSuffix->sizeHint());
00102     label->setBuddy(mSuffix);
00103     connect(mSuffix, TQT_SIGNAL(focusLost()), TQT_SLOT(slotTextLostFocus()));
00104     TQWhatsThis::add(mSuffix,
00105           i18n("Enter text to appear after the person's name in the alarm message, "
00106                "including any necessary leading spaces."));
00107 
00108     TQGroupBox* group = new TQGroupBox(1, Qt::Horizontal, i18n("Select Birthdays"), topWidget);
00109     topLayout->addWidget(group);
00110     mAddresseeList = new BListView(group);
00111     mAddresseeList->setMultiSelection(true);
00112     mAddresseeList->setSelectionMode(TQListView::Extended);
00113     mAddresseeList->setAllColumnsShowFocus(true);
00114     mAddresseeList->setFullWidth(true);
00115     mAddresseeList->addColumn(i18n("Name"));
00116     mAddresseeList->addColumn(i18n("Birthday"));
00117     connect(mAddresseeList, TQT_SIGNAL(selectionChanged()), TQT_SLOT(slotSelectionChanged()));
00118     TQWhatsThis::add(mAddresseeList,
00119           i18n("Select birthdays to set alarms for.\n"
00120                "This list shows all birthdays in KAddressBook except those for which alarms already exist.\n\n"
00121                "You can select multiple birthdays at one time by dragging the mouse over the list, "
00122                "or by clicking the mouse while pressing Ctrl or Shift."));
00123 
00124     group = new TQGroupBox(i18n("Alarm Configuration"), topWidget);
00125     topLayout->addWidget(group);
00126     TQBoxLayout* groupLayout = new TQVBoxLayout(group, marginHint(), spacingHint());
00127     groupLayout->addSpacing(fontMetrics().lineSpacing()/2);
00128 
00129     // Font and colour choice button and sample text
00130     mFontColourButton = new FontColourButton(group);
00131     mFontColourButton->setMaximumHeight(mFontColourButton->sizeHint().height() * 3/2);
00132     groupLayout->addWidget(mFontColourButton);
00133 
00134     // Sound checkbox and file selector
00135     mSoundPicker = new SoundPicker(group);
00136     mSoundPicker->setFixedSize(mSoundPicker->sizeHint());
00137     groupLayout->addWidget(mSoundPicker, 0, TQt::AlignAuto);
00138 
00139     // How much to advance warning to give
00140     mReminder = new Reminder(i18n("&Reminder"),
00141                              i18n("Check to display a reminder in advance of the birthday."),
00142                              i18n("Enter the number of days before each birthday to display a reminder. "
00143                                   "This is in addition to the alarm which is displayed on the birthday."),
00144                              false, false, group);
00145     mReminder->setFixedSize(mReminder->sizeHint());
00146     mReminder->setMaximum(0, 364);
00147     mReminder->setMinutes(0, true);
00148     groupLayout->addWidget(mReminder, 0, TQt::AlignAuto);
00149 
00150     // Acknowledgement confirmation required - default = no confirmation
00151     TQHBoxLayout* layout = new TQHBoxLayout(groupLayout, 2*spacingHint());
00152     mConfirmAck = EditAlarmDlg::createConfirmAckCheckbox(group);
00153     layout->addWidget(mConfirmAck);
00154     layout->addSpacing(2*spacingHint());
00155     layout->addStretch();
00156 
00157     if (ShellProcess::authorised())    // don't display if shell commands not allowed (e.g. kiosk mode)
00158     {
00159         // Special actions button
00160         mSpecialActionsButton = new SpecialActionsButton(i18n("Special Actions..."), group);
00161         layout->addWidget(mSpecialActionsButton);
00162     }
00163 
00164     // Late display checkbox - default = allow late display
00165     layout = new TQHBoxLayout(groupLayout, 2*spacingHint());
00166     mLateCancel = new LateCancelSelector(false, group);
00167     layout->addWidget(mLateCancel);
00168     layout->addStretch();
00169 
00170     // Sub-repetition button
00171     mSubRepetition = new RepetitionButton(i18n("Sub-Repetition"), false, group);
00172     mSubRepetition->set(0, 0, true, 364*24*60);
00173     TQWhatsThis::add(mSubRepetition, i18n("Set up an additional alarm repetition"));
00174     layout->addWidget(mSubRepetition);
00175 
00176     // Set the values to their defaults
00177     mFontColourButton->setDefaultFont();
00178     mFontColourButton->setBgColour(Preferences::defaultBgColour());
00179     mFontColourButton->setFgColour(Preferences::defaultFgColour());     // set colour before setting alarm type buttons
00180     mLateCancel->setMinutes(Preferences::defaultLateCancel(), true, TimePeriod::DAYS);
00181     mConfirmAck->setChecked(Preferences::defaultConfirmAck());
00182     mSoundPicker->set(Preferences::defaultSoundType(), Preferences::defaultSoundFile(),
00183                       Preferences::defaultSoundVolume(), -1, 0, Preferences::defaultSoundRepeat());
00184     if (mSpecialActionsButton)
00185         mSpecialActionsButton->setActions(Preferences::defaultPreAction(), Preferences::defaultPostAction());
00186 
00187     // Initialise the birthday selection list and disable the OK button
00188     loadAddressBook();
00189 }
00190 
00191 /******************************************************************************
00192 * Load the address book in preparation for displaying the birthday selection list.
00193 */
00194 void BirthdayDlg::loadAddressBook()
00195 {
00196     if (!mAddressBook)
00197     {
00198 #if KDE_IS_VERSION(3,1,90)
00199             mAddressBook = KABC::StdAddressBook::self(true);
00200         if (mAddressBook)
00201                     connect(mAddressBook, TQT_SIGNAL(addressBookChanged(AddressBook*)), TQT_SLOT(updateSelectionList()));
00202 #else
00203         mAddressBook = KABC::StdAddressBook::self();
00204         if (mAddressBook)
00205             updateSelectionList();
00206 #endif
00207     }
00208     else
00209         updateSelectionList();
00210         if (!mAddressBook)
00211                 KMessageBox::error(this, i18n("Error reading address book"));
00212 }
00213 
00214 /******************************************************************************
00215 * Close the address book.This is called at program termination.
00216 */
00217 void BirthdayDlg::close()
00218 {
00219     if (mAddressBook)
00220     {
00221         KABC::StdAddressBook::close();
00222         mAddressBook = 0;
00223     }
00224 }
00225 
00226 /******************************************************************************
00227 * Initialise or update the birthday selection list by fetching all birthdays
00228 * from the address book and displaying those which do not already have alarms.
00229 */
00230 void BirthdayDlg::updateSelectionList()
00231 {
00232     // Compile a list of all pending alarm messages which look like birthdays
00233     TQStringList messageList;
00234     KAEvent event;
00235     Event::List events = AlarmCalendar::activeCalendar()->events();
00236     for (Event::List::ConstIterator it = events.begin();  it != events.end();  ++it)
00237     {
00238         Event* kcalEvent = *it;
00239         event.set(*kcalEvent);
00240         if (event.action() == KAEvent::MESSAGE
00241         &&  event.recurType() == KARecurrence::ANNUAL_DATE
00242         &&  (mPrefixText.isEmpty()  ||  event.message().startsWith(mPrefixText)))
00243             messageList.append(event.message());
00244     }
00245 
00246     // Fetch all birthdays from the address book
00247     for (KABC::AddressBook::ConstIterator abit = mAddressBook->begin();  abit != mAddressBook->end();  ++abit)
00248     {
00249         const KABC::Addressee& addressee = *abit;
00250         if (addressee.birthday().isValid())
00251         {
00252             // Create a list entry for this birthday
00253             TQDate birthday = addressee.birthday().date();
00254             TQString name = addressee.nickName();
00255             if (name.isEmpty())
00256                 name = addressee.realName();
00257             // Check if the birthday already has an alarm
00258             TQString text = mPrefixText + name + mSuffixText;
00259             bool alarmExists = (messageList.find(text) != messageList.end());
00260             // Check if the birthday is already in the selection list
00261             bool inSelectionList = false;
00262             AddresseeItem* item = 0;
00263             for (TQListViewItem* qitem = mAddresseeList->firstChild();  qitem;  qitem = qitem->nextSibling())
00264             {
00265                 item = dynamic_cast<AddresseeItem*>(qitem);
00266                 if (item  &&  item->text(AddresseeItem::NAME) == name  &&  item->birthday() == birthday)
00267                 {
00268                     inSelectionList = true;
00269                     break;
00270                 }
00271             }
00272 
00273             if (alarmExists  &&  inSelectionList)
00274                 delete item;     // alarm exists, so remove from selection list
00275             else if (!alarmExists  &&  !inSelectionList)
00276                 new AddresseeItem(mAddresseeList, name, birthday);   // add to list
00277         }
00278     }
00279 //  mAddresseeList->setUpdatesEnabled(true);
00280 
00281     // Enable/disable OK button according to whether anything is currently selected
00282     bool selection = false;
00283     for (TQListViewItem* item = mAddresseeList->firstChild();  item;  item = item->nextSibling())
00284         if (mAddresseeList->isSelected(item))
00285         {
00286             selection = true;
00287             break;
00288         }
00289     enableButtonOK(selection);
00290 }
00291 
00292 /******************************************************************************
00293 * Return a list of events for birthdays chosen.
00294 */
00295 TQValueList<KAEvent> BirthdayDlg::events() const
00296 {
00297     TQValueList<KAEvent> list;
00298     TQDate today = TQDate::currentDate();
00299     TQDateTime todayNoon(today, TQTime(12, 0, 0));
00300     int thisYear = today.year();
00301     int reminder = mReminder->minutes();
00302 
00303     for (TQListViewItem* item = mAddresseeList->firstChild();  item;  item = item->nextSibling())
00304     {
00305         if (mAddresseeList->isSelected(item))
00306         {
00307             AddresseeItem* aItem = dynamic_cast<AddresseeItem*>(item);
00308             if (aItem)
00309             {
00310                 TQDate date = aItem->birthday();
00311                 date.setYMD(thisYear, date.month(), date.day());
00312                 if (date <= today)
00313                     date.setYMD(thisYear + 1, date.month(), date.day());
00314                 KAEvent event(date,
00315                               mPrefix->text() + aItem->text(AddresseeItem::NAME) + mSuffix->text(),
00316                               mFontColourButton->bgColour(), mFontColourButton->fgColour(),
00317                               mFontColourButton->font(), KAEvent::MESSAGE, mLateCancel->minutes(),
00318                               mFlags);
00319                 float fadeVolume;
00320                 int   fadeSecs;
00321                 float volume = mSoundPicker->volume(fadeVolume, fadeSecs);
00322                 event.setAudioFile(mSoundPicker->file(), volume, fadeVolume, fadeSecs);
00323                 TQValueList<int> months;
00324                 months.append(date.month());
00325                 event.setRecurAnnualByDate(1, months, 0, Preferences::defaultFeb29Type(), -1, TQDate());
00326                 event.setRepetition(mSubRepetition->interval(), mSubRepetition->count());
00327                 event.setNextOccurrence(todayNoon);
00328                 if (reminder)
00329                     event.setReminder(reminder, false);
00330                 if (mSpecialActionsButton)
00331                     event.setActions(mSpecialActionsButton->preAction(),
00332                                      mSpecialActionsButton->postAction());
00333                 list.append(event);
00334             }
00335         }
00336     }
00337     return list;
00338 }
00339 
00340 /******************************************************************************
00341 * Called when the OK button is selected to import the selected birthdays.
00342 */
00343 void BirthdayDlg::slotOk()
00344 {
00345     // Save prefix and suffix texts to use as future defaults
00346     KConfig* config = kapp->config();
00347     config->setGroup(TQString::fromLatin1("General"));
00348     config->writeEntry(TQString::fromLatin1("BirthdayPrefix"), mPrefix->text());
00349     config->writeEntry(TQString::fromLatin1("BirthdaySuffix"), mSuffix->text());
00350     config->sync();
00351 
00352     mFlags = (mSoundPicker->sound() == SoundPicker::BEEP ? KAEvent::BEEP : 0)
00353            | (mSoundPicker->repeat()                     ? KAEvent::REPEAT_SOUND : 0)
00354            | (mConfirmAck->isChecked()                   ? KAEvent::CONFIRM_ACK : 0)
00355            | (mFontColourButton->defaultFont()           ? KAEvent::DEFAULT_FONT : 0)
00356            |                                               KAEvent::ANY_TIME;
00357     KDialogBase::slotOk();
00358 }
00359 
00360 /******************************************************************************
00361 * Called when the group of items selected changes.
00362 * Enable/disable the OK button depending on whether anything is selected.
00363 */
00364 void BirthdayDlg::slotSelectionChanged()
00365 {
00366     for (TQListViewItem* item = mAddresseeList->firstChild();  item;  item = item->nextSibling())
00367         if (mAddresseeList->isSelected(item))
00368         {
00369             enableButtonOK(true);
00370             return;
00371         }
00372     enableButtonOK(false);
00373 
00374 }
00375 
00376 /******************************************************************************
00377 * Called when the prefix or suffix text has lost keyboard focus.
00378 * If the text has changed, re-evaluates the selection list according to the new
00379 * birthday alarm text format.
00380 */
00381 void BirthdayDlg::slotTextLostFocus()
00382 {
00383     TQString prefix = mPrefix->text();
00384     TQString suffix = mSuffix->text();
00385     if (prefix != mPrefixText  ||  suffix != mSuffixText)
00386     {
00387         // Text has changed - re-evaluate the selection list
00388         mPrefixText = prefix;
00389         mSuffixText = suffix;
00390         loadAddressBook();
00391     }
00392 }
00393 
00394 
00395 /*=============================================================================
00396 = Class: AddresseeItem
00397 =============================================================================*/
00398 
00399 AddresseeItem::AddresseeItem(TQListView* parent, const TQString& name, const TQDate& birthday)
00400     : TQListViewItem(parent),
00401       mBirthday(birthday)
00402 {
00403     setText(NAME, name);
00404     setText(BIRTHDAY, KGlobal::locale()->formatDate(mBirthday, true));
00405     mBirthdayOrder.sprintf("%04d%03d", mBirthday.year(), mBirthday.dayOfYear());
00406 }
00407 
00408 TQString AddresseeItem::key(int column, bool) const
00409 {
00410     if (column == BIRTHDAY)
00411         return mBirthdayOrder;
00412     return text(column).lower();
00413 }
00414 
00415 
00416 /*=============================================================================
00417 = Class: BListView
00418 =============================================================================*/
00419 
00420 BListView::BListView(TQWidget* parent, const char* name)
00421     : KListView(parent, name)
00422 {
00423     KAccel* accel = new KAccel(this);
00424     accel->insert(KStdAccel::SelectAll, TQT_TQOBJECT(this), TQT_SLOT(slotSelectAll()));
00425     accel->insert(KStdAccel::Deselect, TQT_TQOBJECT(this), TQT_SLOT(slotDeselect()));
00426     accel->readSettings();
00427 }