2 * birthdaydlg.cpp - dialog to pick birthdays from address book
4 * Copyright © 2002-2012 by David Jarvie <djarvie@kde.org>
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.
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.
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.
21 #include "birthdaydlg.h"
24 #include "alarmcalendar.h"
25 #include "birthdaymodel.h"
27 #include "editdlgtypes.h"
28 #include "fontcolourbutton.h"
29 #include "kalarmapp.h"
30 #include "latecancel.h"
31 #include "preferences.h"
33 #include "repetitionbutton.h"
34 #include "shellprocess.h"
35 #include "soundpicker.h"
36 #include "specialactions.h"
38 #include <AkonadiWidgets/controlgui.h>
39 #include <AkonadiCore/entitymimetypefiltermodel.h>
41 #include <KLocalizedString>
42 #include <kconfiggroup.h>
43 #include <kmessagebox.h>
44 #include <kstandardaction.h>
45 #include <kactioncollection.h>
46 #include <kdescendantsproxymodel.h>
47 #include <KSharedConfig>
53 #include <QHeaderView>
54 #include <QHBoxLayout>
55 #include <QVBoxLayout>
56 #include <QDialogButtonBox>
57 #include "kalarm_debug.h"
62 BirthdayDlg::BirthdayDlg(QWidget
* parent
)
64 mSpecialActionsButton(Q_NULLPTR
)
66 setObjectName(QStringLiteral("BirthdayDlg")); // used by LikeBack
67 setWindowTitle(i18nc("@title:window", "Import Birthdays From KAddressBook"));
69 QVBoxLayout
* topLayout
= new QVBoxLayout(this);
70 topLayout
->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
72 // Prefix and suffix to the name in the alarm text
73 // Get default prefix and suffix texts from config file
74 KConfigGroup
config(KSharedConfig::openConfig(), "General");
75 mPrefixText
= config
.readEntry("BirthdayPrefix", i18nc("@info", "Birthday: "));
76 mSuffixText
= config
.readEntry("BirthdaySuffix");
78 QGroupBox
* textGroup
= new QGroupBox(i18nc("@title:group", "Alarm Text"), this);
79 topLayout
->addWidget(textGroup
);
80 QGridLayout
* grid
= new QGridLayout(textGroup
);
81 grid
->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin
));
82 grid
->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
83 QLabel
* label
= new QLabel(i18nc("@label:textbox", "Prefix:"), textGroup
);
84 label
->setFixedSize(label
->sizeHint());
85 grid
->addWidget(label
, 0, 0);
86 mPrefix
= new BLineEdit(mPrefixText
, textGroup
);
87 mPrefix
->setMinimumSize(mPrefix
->sizeHint());
88 label
->setBuddy(mPrefix
);
89 connect(mPrefix
, &BLineEdit::focusLost
, this, &BirthdayDlg::slotTextLostFocus
);
90 mPrefix
->setWhatsThis(i18nc("@info:whatsthis",
91 "Enter text to appear before the person's name in the alarm message, "
92 "including any necessary trailing spaces."));
93 grid
->addWidget(mPrefix
, 0, 1);
95 label
= new QLabel(i18nc("@label:textbox", "Suffix:"), textGroup
);
96 label
->setFixedSize(label
->sizeHint());
97 grid
->addWidget(label
, 1, 0);
98 mSuffix
= new BLineEdit(mSuffixText
, textGroup
);
99 mSuffix
->setMinimumSize(mSuffix
->sizeHint());
100 label
->setBuddy(mSuffix
);
101 connect(mSuffix
, &BLineEdit::focusLost
, this, &BirthdayDlg::slotTextLostFocus
);
102 mSuffix
->setWhatsThis(i18nc("@info:whatsthis",
103 "Enter text to appear after the person's name in the alarm message, "
104 "including any necessary leading spaces."));
105 grid
->addWidget(mSuffix
, 1, 1);
107 QGroupBox
* group
= new QGroupBox(i18nc("@title:group", "Select Birthdays"), this);
108 topLayout
->addWidget(group
);
109 QVBoxLayout
* layout
= new QVBoxLayout(group
);
110 layout
->setMargin(0);
112 // Start Akonadi server as we need it for the birthday model to access contacts information
113 Akonadi::ControlGui::start();
115 BirthdayModel
* model
= BirthdayModel::instance();
116 connect(model
, &BirthdayModel::dataChanged
, this, &BirthdayDlg::resizeViewColumns
);
118 KDescendantsProxyModel
* descendantsModel
= new KDescendantsProxyModel(this);
119 descendantsModel
->setSourceModel(model
);
121 Akonadi::EntityMimeTypeFilterModel
* mimeTypeFilter
= new Akonadi::EntityMimeTypeFilterModel(this);
122 mimeTypeFilter
->setSourceModel(descendantsModel
);
123 mimeTypeFilter
->addMimeTypeExclusionFilter(Akonadi::Collection::mimeType());
124 mimeTypeFilter
->setHeaderGroup(Akonadi::EntityTreeModel::ItemListHeaders
);
126 mBirthdaySortModel
= new BirthdaySortModel(this);
127 mBirthdaySortModel
->setSourceModel(mimeTypeFilter
);
128 mBirthdaySortModel
->setSortCaseSensitivity(Qt::CaseInsensitive
);
129 mBirthdaySortModel
->setPrefixSuffix(mPrefixText
, mSuffixText
);
130 mListView
= new QTreeView(group
);
131 mListView
->setEditTriggers(QAbstractItemView::NoEditTriggers
);
132 mListView
->setModel(mBirthdaySortModel
);
133 mListView
->setRootIsDecorated(false); // don't show expander icons
134 mListView
->setSortingEnabled(true);
135 mListView
->sortByColumn(BirthdayModel::NameColumn
);
136 mListView
->setAllColumnsShowFocus(true);
137 mListView
->setSelectionMode(QAbstractItemView::ExtendedSelection
);
138 mListView
->setSelectionBehavior(QAbstractItemView::SelectRows
);
139 mListView
->setTextElideMode(Qt::ElideRight
);
140 mListView
->header()->setResizeMode(BirthdayModel::NameColumn
, QHeaderView::Stretch
);
141 mListView
->header()->setResizeMode(BirthdayModel::DateColumn
, QHeaderView::ResizeToContents
);
142 connect(mListView
->selectionModel(), &QItemSelectionModel::selectionChanged
, this, &BirthdayDlg::slotSelectionChanged
);
143 mListView
->setWhatsThis(xi18nc("@info:whatsthis",
144 "<para>Select birthdays to set alarms for.<nl/>"
145 "This list shows all birthdays in <application>KAddressBook</application> except those for which alarms already exist.</para>"
146 "<para>You can select multiple birthdays at one time by dragging the mouse over the list, "
147 "or by clicking the mouse while pressing Ctrl or Shift.</para>"));
148 layout
->addWidget(mListView
);
150 group
= new QGroupBox(i18nc("@title:group", "Alarm Configuration"), this);
151 topLayout
->addWidget(group
);
152 QVBoxLayout
* groupLayout
= new QVBoxLayout(group
);
153 groupLayout
->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin
));
154 groupLayout
->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
156 // Sound checkbox and file selector
157 QHBoxLayout
* hlayout
= new QHBoxLayout();
158 hlayout
->setMargin(0);
159 groupLayout
->addLayout(hlayout
);
160 mSoundPicker
= new SoundPicker(group
);
161 mSoundPicker
->setFixedSize(mSoundPicker
->sizeHint());
162 hlayout
->addWidget(mSoundPicker
);
163 hlayout
->addSpacing(2 * style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
164 hlayout
->addStretch();
166 // Font and colour choice button and sample text
167 mFontColourButton
= new FontColourButton(group
);
168 mFontColourButton
->setMaximumHeight(mFontColourButton
->sizeHint().height() * 3/2);
169 hlayout
->addWidget(mFontColourButton
);
170 connect(mFontColourButton
, &FontColourButton::selected
, this, &BirthdayDlg::setColours
);
172 // How much advance warning to give
173 mReminder
= new Reminder(i18nc("@info:whatsthis", "Check to display a reminder in advance of or after the birthday."),
174 i18nc("@info:whatsthis", "Enter the number of days before or after each birthday to display a reminder. "
175 "This is in addition to the alarm which is displayed on the birthday."),
176 i18nc("@info:whatsthis", "Select whether the reminder should be triggered before or after the birthday."),
177 false, false, group
);
178 mReminder
->setFixedSize(mReminder
->sizeHint());
179 mReminder
->setMaximum(0, 364);
180 mReminder
->setMinutes(0, true);
181 groupLayout
->addWidget(mReminder
, 0, Qt::AlignLeft
);
183 // Acknowledgement confirmation required - default = no confirmation
184 hlayout
= new QHBoxLayout();
185 hlayout
->setMargin(0);
186 hlayout
->setSpacing(2 * style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
187 groupLayout
->addLayout(hlayout
);
188 mConfirmAck
= EditDisplayAlarmDlg::createConfirmAckCheckbox(group
);
189 mConfirmAck
->setFixedSize(mConfirmAck
->sizeHint());
190 hlayout
->addWidget(mConfirmAck
);
191 hlayout
->addSpacing(2 * style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
192 hlayout
->addStretch();
194 if (ShellProcess::authorised()) // don't display if shell commands not allowed (e.g. kiosk mode)
196 // Special actions button
197 mSpecialActionsButton
= new SpecialActionsButton(false, group
);
198 mSpecialActionsButton
->setFixedSize(mSpecialActionsButton
->sizeHint());
199 hlayout
->addWidget(mSpecialActionsButton
);
202 // Late display checkbox - default = allow late display
203 hlayout
= new QHBoxLayout();
204 hlayout
->setMargin(0);
205 hlayout
->setSpacing(2 * style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
206 groupLayout
->addLayout(hlayout
);
207 mLateCancel
= new LateCancelSelector(false, group
);
208 mLateCancel
->setFixedSize(mLateCancel
->sizeHint());
209 hlayout
->addWidget(mLateCancel
);
210 hlayout
->addStretch();
212 // Sub-repetition button
213 mSubRepetition
= new RepetitionButton(i18nc("@action:button", "Sub-Repetition"), false, group
);
214 mSubRepetition
->setFixedSize(mSubRepetition
->sizeHint());
215 mSubRepetition
->set(Repetition(), true, 364*24*60);
216 mSubRepetition
->setWhatsThis(i18nc("@info:whatsthis", "Set up an additional alarm repetition"));
217 hlayout
->addWidget(mSubRepetition
);
219 // Set the values to their defaults
220 setColours(Preferences::defaultFgColour(), Preferences::defaultBgColour());
221 mFontColourButton
->setDefaultFont();
222 mFontColourButton
->setBgColour(Preferences::defaultBgColour());
223 mFontColourButton
->setFgColour(Preferences::defaultFgColour());
224 mLateCancel
->setMinutes(Preferences::defaultLateCancel(), true, TimePeriod::Days
);
225 mConfirmAck
->setChecked(Preferences::defaultConfirmAck());
226 mSoundPicker
->set(Preferences::defaultSoundType(), Preferences::defaultSoundFile(),
227 Preferences::defaultSoundVolume(), -1, 0, Preferences::defaultSoundRepeat());
228 if (mSpecialActionsButton
)
230 KAEvent::ExtraActionOptions
opts(0);
231 if (Preferences::defaultExecPreActionOnDeferral())
232 opts
|= KAEvent::ExecPreActOnDeferral
;
233 if (Preferences::defaultCancelOnPreActionError())
234 opts
|= KAEvent::CancelOnPreActError
;
235 if (Preferences::defaultDontShowPreActionError())
236 opts
|= KAEvent::DontShowPreActError
;
237 mSpecialActionsButton
->setActions(Preferences::defaultPreAction(), Preferences::defaultPostAction(), opts
);
241 mButtonBox
= new QDialogButtonBox(this);
242 mButtonBox
->addButton(QDialogButtonBox::Ok
);
243 mButtonBox
->addButton(QDialogButtonBox::Cancel
);
244 connect(mButtonBox
, &QDialogButtonBox::accepted
,
245 this, &BirthdayDlg::slotOk
);
246 connect(mButtonBox
, &QDialogButtonBox::rejected
,
247 this, &QDialog::reject
);
248 topLayout
->addWidget(mButtonBox
);
251 KActionCollection
* actions
= new KActionCollection(this);
252 KStandardAction::selectAll(mListView
, SLOT(selectAll()), actions
);
253 KStandardAction::deselect(mListView
, SLOT(clearSelection()), actions
);
254 actions
->addAssociatedWidget(mListView
);
255 foreach (QAction
* action
, actions
->actions())
256 action
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
258 mButtonBox
->button(QDialogButtonBox::Ok
)->setEnabled(false); // only enable OK button when something is selected
261 /******************************************************************************
262 * Return a list of events for birthdays chosen.
264 QVector
<KAEvent
> BirthdayDlg::events() const
266 QVector
<KAEvent
> list
;
267 QModelIndexList indexes
= mListView
->selectionModel()->selectedRows();
268 int count
= indexes
.count();
271 QDate today
= KDateTime::currentLocalDate();
272 KDateTime
todayStart(today
, KDateTime::Spec(KDateTime::ClockTime
));
273 int thisYear
= today
.year();
274 int reminder
= mReminder
->minutes();
275 for (int i
= 0; i
< count
; ++i
)
277 const QModelIndex nameIndex
= indexes
.at(i
).model()->index(indexes
.at(i
).row(), 0);
278 const QModelIndex birthdayIndex
= indexes
.at(i
).model()->index(indexes
.at(i
).row(), 1);
279 const QString name
= nameIndex
.data(Qt::DisplayRole
).toString();
280 QDate date
= birthdayIndex
.data(BirthdayModel::DateRole
).toDate();
281 date
.setYMD(thisYear
, date
.month(), date
.day());
283 date
.setYMD(thisYear
+ 1, date
.month(), date
.day());
284 KAEvent
event(KDateTime(date
, KDateTime::Spec(KDateTime::ClockTime
)),
285 mPrefix
->text() + name
+ mSuffix
->text(),
286 mFontColourButton
->bgColour(), mFontColourButton
->fgColour(),
287 mFontColourButton
->font(), KAEvent::MESSAGE
, mLateCancel
->minutes(),
291 float volume
= mSoundPicker
->volume(fadeVolume
, fadeSecs
);
292 int repeatPause
= mSoundPicker
->repeatPause();
293 event
.setAudioFile(mSoundPicker
->file().toDisplayString(), volume
, fadeVolume
, fadeSecs
, repeatPause
);
294 QVector
<int> months(1, date
.month());
295 event
.setRecurAnnualByDate(1, months
, 0, KARecurrence::defaultFeb29Type(), -1, QDate());
296 event
.setRepetition(mSubRepetition
->repetition());
297 event
.setNextOccurrence(todayStart
);
299 event
.setReminder(reminder
, false);
300 if (mSpecialActionsButton
)
301 event
.setActions(mSpecialActionsButton
->preAction(),
302 mSpecialActionsButton
->postAction(),
303 mSpecialActionsButton
->options());
310 /******************************************************************************
311 * Called when the OK button is selected to import the selected birthdays.
313 void BirthdayDlg::slotOk()
315 // Save prefix and suffix texts to use as future defaults
316 KConfigGroup
config(KSharedConfig::openConfig(), "General");
317 config
.writeEntry("BirthdayPrefix", mPrefix
->text());
318 config
.writeEntry("BirthdaySuffix", mSuffix
->text());
321 mFlags
= KAEvent::ANY_TIME
;
322 if (mSoundPicker
->sound() == Preferences::Sound_Beep
) mFlags
|= KAEvent::BEEP
;
323 if (mSoundPicker
->repeatPause() >= 0) mFlags
|= KAEvent::REPEAT_SOUND
;
324 if (mConfirmAck
->isChecked()) mFlags
|= KAEvent::CONFIRM_ACK
;
325 if (mFontColourButton
->defaultFont()) mFlags
|= KAEvent::DEFAULT_FONT
;
329 /******************************************************************************
330 * Called when the group of items selected changes.
331 * Enable/disable the OK button depending on whether anything is selected.
333 void BirthdayDlg::slotSelectionChanged()
335 mButtonBox
->button(QDialogButtonBox::Ok
)->setEnabled(mListView
->selectionModel()->hasSelection());
338 /******************************************************************************
339 * Called when the font/color button has been clicked.
340 * Set the colors in the message text entry control.
342 void BirthdayDlg::setColours(const QColor
& fgColour
, const QColor
& bgColour
)
344 QPalette pal
= mPrefix
->palette();
345 pal
.setColor(mPrefix
->backgroundRole(), bgColour
);
346 pal
.setColor(mPrefix
->foregroundRole(), fgColour
);
347 mPrefix
->setPalette(pal
);
348 mSuffix
->setPalette(pal
);
351 /******************************************************************************
352 * Called when the data has changed in the birthday list.
353 * Resize the date column.
355 void BirthdayDlg::resizeViewColumns()
357 mListView
->resizeColumnToContents(BirthdayModel::DateColumn
);
360 /******************************************************************************
361 * Called when the prefix or suffix text has lost keyboard focus.
362 * If the text has changed, re-evaluates the selection list according to the new
363 * birthday alarm text format.
365 void BirthdayDlg::slotTextLostFocus()
367 QString prefix
= mPrefix
->text();
368 QString suffix
= mSuffix
->text();
369 if (prefix
!= mPrefixText
|| suffix
!= mSuffixText
)
371 // Text has changed - re-evaluate the selection list
372 mPrefixText
= prefix
;
373 mSuffixText
= suffix
;
374 mBirthdaySortModel
->setPrefixSuffix(mPrefixText
, mSuffixText
);