Warning, file /pim/kalarm/src/birthdaydlg.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * birthdaydlg.cpp - dialog to pick birthdays from address book 0003 * Program: kalarm 0004 * SPDX-FileCopyrightText: 2002-2023 David Jarvie <djarvie@kde.org> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "birthdaydlg.h" 0010 0011 #include "editdlgtypes.h" 0012 #include "fontcolourbutton.h" 0013 #include "latecancel.h" 0014 #include "preferences.h" 0015 #include "reminder.h" 0016 #include "repetitionbutton.h" 0017 #include "resourcescalendar.h" 0018 #include "soundpicker.h" 0019 #include "specialactions.h" 0020 #include "lib/checkbox.h" 0021 #include "lib/shellprocess.h" 0022 #include "akonadiplugin/akonadiplugin.h" 0023 0024 #include <KLocalizedString> 0025 #include <KConfigGroup> 0026 #include <KStandardAction> 0027 #include <KActionCollection> 0028 #include <KSharedConfig> 0029 0030 #include <QAction> 0031 #include <QGroupBox> 0032 #include <QLabel> 0033 #include <QTreeView> 0034 #include <QHeaderView> 0035 #include <QHBoxLayout> 0036 #include <QVBoxLayout> 0037 #include <QDialogButtonBox> 0038 #include <QSortFilterProxyModel> 0039 0040 namespace 0041 { 0042 const QLatin1String GENERAL_GROUP("General"); 0043 } 0044 0045 BirthdayDlg::BirthdayDlg(QWidget* parent) 0046 : QDialog(parent) 0047 { 0048 setObjectName(QLatin1StringView("BirthdayDlg")); // used by LikeBack 0049 setWindowTitle(i18nc("@title:window", "Import Birthdays From KAddressBook")); 0050 0051 auto mainLayout = new QVBoxLayout(this); 0052 QWidget* mainWidget = new QWidget; 0053 mainLayout->addWidget(mainWidget); 0054 0055 auto topLayout = new QVBoxLayout(mainWidget); 0056 topLayout->setContentsMargins(0, 0, 0, 0); 0057 0058 if (Preferences::useAlarmName()) 0059 { 0060 auto hlayout = new QHBoxLayout(); 0061 hlayout->setContentsMargins(0, 0, 0, 0); 0062 topLayout->addLayout(hlayout); 0063 QLabel* label = new QLabel(i18nc("@label:textbox", "Alarm name:"), mainWidget); 0064 hlayout->addWidget(label); 0065 mName = new KLineEdit(mainWidget); 0066 mName->setMinimumSize(mName->sizeHint()); 0067 label->setBuddy(mName); 0068 mName->setWhatsThis(i18nc("@info:whatsthis", "Enter a name to help you identify this alarm. This is optional and need not be unique.")); 0069 hlayout->addWidget(mName); 0070 } 0071 0072 // Prefix and suffix to the name in the alarm text 0073 // Get default prefix and suffix texts from config file 0074 KConfigGroup config(KSharedConfig::openConfig(), GENERAL_GROUP); 0075 mPrefixText = config.readEntry("BirthdayPrefix", i18nc("@info", "Birthday: ")); 0076 mSuffixText = config.readEntry("BirthdaySuffix"); 0077 0078 QGroupBox* textGroup = new QGroupBox(i18nc("@title:group", "Alarm Text"), mainWidget); 0079 topLayout->addWidget(textGroup); 0080 auto grid = new QGridLayout(textGroup); 0081 QLabel* label = new QLabel(i18nc("@label:textbox", "Prefix:"), textGroup); 0082 grid->addWidget(label, 0, 0); 0083 mPrefix = new BLineEdit(mPrefixText, textGroup); 0084 mPrefix->setMinimumSize(mPrefix->sizeHint()); 0085 label->setBuddy(mPrefix); 0086 connect(mPrefix, &BLineEdit::focusLost, this, &BirthdayDlg::slotTextLostFocus); 0087 mPrefix->setWhatsThis(i18nc("@info:whatsthis", 0088 "Enter text to appear before the person's name in the alarm message, " 0089 "including any necessary trailing spaces.")); 0090 grid->addWidget(mPrefix, 0, 1); 0091 0092 label = new QLabel(i18nc("@label:textbox", "Suffix:"), textGroup); 0093 grid->addWidget(label, 1, 0); 0094 mSuffix = new BLineEdit(mSuffixText, textGroup); 0095 mSuffix->setMinimumSize(mSuffix->sizeHint()); 0096 label->setBuddy(mSuffix); 0097 connect(mSuffix, &BLineEdit::focusLost, this, &BirthdayDlg::slotTextLostFocus); 0098 mSuffix->setWhatsThis(i18nc("@info:whatsthis", 0099 "Enter text to appear after the person's name in the alarm message, " 0100 "including any necessary leading spaces.")); 0101 grid->addWidget(mSuffix, 1, 1); 0102 0103 QGroupBox* group = new QGroupBox(i18nc("@title:group", "Select Birthdays"), mainWidget); 0104 topLayout->addWidget(group); 0105 auto layout = new QVBoxLayout(group); 0106 layout->setContentsMargins(0, 0, 0, 0); 0107 0108 AkonadiPlugin* akonadiPlugin = Preferences::akonadiPlugin(); 0109 if (!akonadiPlugin) 0110 return; // no error message - this constructor should only be called if using Akonadi plugin 0111 mBirthdaySortModel = akonadiPlugin->createBirthdayModels(mainWidget, this); 0112 connect(akonadiPlugin, &AkonadiPlugin::birthdayModelDataChanged, this, &BirthdayDlg::resizeViewColumns); 0113 setSortModelSelectionList(); 0114 0115 mBirthdayModel_NameColumn = akonadiPlugin->birthdayModelEnum(AkonadiPlugin::BirthdayModelValue::NameColumn); 0116 mBirthdayModel_DateColumn = akonadiPlugin->birthdayModelEnum(AkonadiPlugin::BirthdayModelValue::DateColumn); 0117 mBirthdayModel_DateRole = akonadiPlugin->birthdayModelEnum(AkonadiPlugin::BirthdayModelValue::DateRole); 0118 0119 mListView = new QTreeView(group); 0120 mListView->setEditTriggers(QAbstractItemView::NoEditTriggers); 0121 mListView->setModel(mBirthdaySortModel); 0122 mListView->setRootIsDecorated(false); // don't show expander icons 0123 mListView->setSortingEnabled(true); 0124 mListView->sortByColumn(mBirthdayModel_NameColumn, mListView->header()->sortIndicatorOrder()); 0125 mListView->setAllColumnsShowFocus(true); 0126 mListView->setSelectionMode(QAbstractItemView::ExtendedSelection); 0127 mListView->setSelectionBehavior(QAbstractItemView::SelectRows); 0128 mListView->setTextElideMode(Qt::ElideRight); 0129 mListView->header()->setSectionResizeMode(mBirthdayModel_NameColumn, QHeaderView::Stretch); 0130 mListView->header()->setSectionResizeMode(mBirthdayModel_DateColumn, QHeaderView::ResizeToContents); 0131 connect(mListView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &BirthdayDlg::slotSelectionChanged); 0132 mListView->setWhatsThis(xi18nc("@info:whatsthis", 0133 "<para>Select birthdays to set alarms for.<nl/>" 0134 "This list shows all birthdays in <application>KAddressBook</application> except those for which alarms already exist.</para>" 0135 "<para>You can select multiple birthdays at one time by dragging the mouse over the list, " 0136 "or by clicking the mouse while pressing Ctrl or Shift.</para>")); 0137 layout->addWidget(mListView); 0138 0139 group = new QGroupBox(i18nc("@title:group", "Alarm Configuration"), mainWidget); 0140 topLayout->addWidget(group); 0141 auto groupLayout = new QVBoxLayout(group); 0142 0143 // Sound checkbox and file selector 0144 auto hlayout = new QHBoxLayout(); 0145 hlayout->setContentsMargins(0, 0, 0, 0); 0146 groupLayout->addLayout(hlayout); 0147 mSoundPicker = new SoundPicker(group); 0148 hlayout->addWidget(mSoundPicker); 0149 hlayout->addSpacing(2 * style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing)); 0150 hlayout->addStretch(); 0151 0152 // Font and colour choice button and sample text 0153 mFontColourButton = new FontColourButton(group); 0154 mFontColourButton->setMaximumHeight(mFontColourButton->sizeHint().height() * 3/2); 0155 hlayout->addWidget(mFontColourButton); 0156 connect(mFontColourButton, &FontColourButton::selected, this, &BirthdayDlg::setColours); 0157 0158 // How much advance warning to give 0159 mReminder = new Reminder(i18nc("@info:whatsthis", "Check to display a reminder in advance of or after the birthday."), 0160 i18nc("@info:whatsthis", "Enter the number of days before or after each birthday to display a reminder. " 0161 "This is in addition to the alarm which is displayed on the birthday."), 0162 i18nc("@info:whatsthis", "Select whether the reminder should be triggered before or after the birthday."), 0163 false, false, group); 0164 mReminder->setMaximum(0, 364); 0165 mReminder->setMinutes(0, true); 0166 groupLayout->addWidget(mReminder, 0, Qt::AlignLeft); 0167 0168 // Acknowledgement confirmation required - default = no confirmation 0169 hlayout = new QHBoxLayout(); 0170 hlayout->setContentsMargins(0, 0, 0, 0); 0171 hlayout->setSpacing(2 * style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing)); 0172 groupLayout->addLayout(hlayout); 0173 mConfirmAck = EditDisplayAlarmDlg::createConfirmAckCheckbox(group); 0174 hlayout->addWidget(mConfirmAck); 0175 hlayout->addSpacing(2 * style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing)); 0176 hlayout->addStretch(); 0177 0178 if (ShellProcess::authorised()) // don't display if shell commands not allowed (e.g. kiosk mode) 0179 { 0180 // Special actions button 0181 mSpecialActionsButton = new SpecialActionsButton(false, group); 0182 hlayout->addWidget(mSpecialActionsButton); 0183 } 0184 0185 // Late display checkbox - default = allow late display 0186 hlayout = new QHBoxLayout(); 0187 hlayout->setContentsMargins(0, 0, 0, 0); 0188 hlayout->setSpacing(2 * style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing)); 0189 groupLayout->addLayout(hlayout); 0190 mLateCancel = new LateCancelSelector(false, group); 0191 hlayout->addWidget(mLateCancel); 0192 hlayout->addStretch(); 0193 0194 // Sub-repetition button 0195 mSubRepetition = new RepetitionButton(i18nc("@action:button", "Sub-Repetition"), false, group); 0196 mSubRepetition->set(Repetition(), true, 364*24*60); 0197 mSubRepetition->setWhatsThis(i18nc("@info:whatsthis", "Set up an additional alarm repetition")); 0198 hlayout->addWidget(mSubRepetition); 0199 0200 // Set the values to their defaults 0201 setColours(Preferences::defaultFgColour(), Preferences::defaultBgColour()); 0202 mFontColourButton->setFont(Preferences::messageFont(), true); 0203 mFontColourButton->setBgColour(Preferences::defaultBgColour()); 0204 mFontColourButton->setFgColour(Preferences::defaultFgColour()); 0205 mLateCancel->setMinutes(Preferences::defaultLateCancel(), true, TimePeriod::Days); 0206 mConfirmAck->setChecked(Preferences::defaultConfirmAck()); 0207 mSoundPicker->set(Preferences::defaultSoundType(), Preferences::defaultSoundFile(), 0208 Preferences::defaultSoundVolume(), -1, 0, Preferences::defaultSoundRepeat()); 0209 if (mSpecialActionsButton) 0210 { 0211 KAEvent::ExtraActionOptions opts{}; 0212 if (Preferences::defaultExecPreActionOnDeferral()) 0213 opts |= KAEvent::ExecPreActOnDeferral; 0214 if (Preferences::defaultCancelOnPreActionError()) 0215 opts |= KAEvent::CancelOnPreActError; 0216 if (Preferences::defaultDontShowPreActionError()) 0217 opts |= KAEvent::DontShowPreActError; 0218 mSpecialActionsButton->setActions(Preferences::defaultPreAction(), Preferences::defaultPostAction(), opts); 0219 } 0220 0221 0222 mButtonBox = new QDialogButtonBox(this); 0223 mButtonBox->addButton(QDialogButtonBox::Ok); 0224 mButtonBox->addButton(QDialogButtonBox::Cancel); 0225 connect(mButtonBox, &QDialogButtonBox::accepted, 0226 this, &BirthdayDlg::slotOk); 0227 connect(mButtonBox, &QDialogButtonBox::rejected, 0228 this, &QDialog::reject); 0229 mainLayout->addWidget(mButtonBox); 0230 0231 0232 KActionCollection* actions = new KActionCollection(this); 0233 KStandardAction::selectAll(mListView, &QTreeView::selectAll, actions); 0234 KStandardAction::deselect(mListView, &QTreeView::clearSelection, actions); 0235 actions->addAssociatedWidget(mListView); 0236 const auto lstActions = actions->actions(); 0237 for (QAction* action : lstActions) 0238 action->setShortcutContext(Qt::WidgetWithChildrenShortcut); 0239 0240 mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false); // only enable OK button when something is selected 0241 } 0242 0243 /****************************************************************************** 0244 * Return a list of events for birthdays chosen. 0245 */ 0246 QList<KAEvent> BirthdayDlg::events() const 0247 { 0248 QList<KAEvent> list; 0249 const QModelIndexList indexes = mListView->selectionModel()->selectedRows(); 0250 const int count = indexes.count(); 0251 if (!count) 0252 return list; 0253 list.reserve(count); 0254 const QDate today = KADateTime::currentLocalDate(); 0255 const KADateTime todayStart(today, KADateTime::LocalZone); 0256 const int thisYear = today.year(); 0257 const int reminder = mReminder->minutes(); 0258 for (const QModelIndex& index : indexes) 0259 { 0260 const QModelIndex nameIndex = index.model()->index(index.row(), 0); 0261 const QModelIndex birthdayIndex = index.model()->index(index.row(), 1); 0262 const QString name = nameIndex.data(Qt::DisplayRole).toString(); 0263 QDate date = birthdayIndex.data(mBirthdayModel_DateRole).toDate(); 0264 date.setDate(thisYear, date.month(), date.day()); 0265 if (date <= today) 0266 date.setDate(thisYear + 1, date.month(), date.day()); 0267 KAEvent event(KADateTime(date, KADateTime::LocalZone), 0268 (mName ? mName->text() : QString()), 0269 mPrefix->text() + name + mSuffix->text(), 0270 mFontColourButton->bgColour(), mFontColourButton->fgColour(), 0271 mFontColourButton->font(), KAEvent::SubAction::Message, mLateCancel->minutes(), 0272 mFlags, true); 0273 float fadeVolume; 0274 int fadeSecs; 0275 const float volume = mSoundPicker->volume(fadeVolume, fadeSecs); 0276 const int repeatPause = mSoundPicker->repeatPause(); 0277 event.setAudioFile(mSoundPicker->file().toDisplayString(), volume, fadeVolume, fadeSecs, repeatPause); 0278 const QList<int> months(1, date.month()); 0279 event.setRecurAnnualByDate(1, months, 0, KARecurrence::defaultFeb29Type(), -1, QDate()); 0280 event.setRepetition(mSubRepetition->repetition()); 0281 event.setNextOccurrence(todayStart); 0282 if (reminder) 0283 event.setReminder(reminder, false); 0284 if (mSpecialActionsButton) 0285 event.setActions(mSpecialActionsButton->preAction(), 0286 mSpecialActionsButton->postAction(), 0287 mSpecialActionsButton->options()); 0288 event.endChanges(); 0289 list.append(event); 0290 } 0291 return list; 0292 } 0293 0294 /****************************************************************************** 0295 * Called when the OK button is selected to import the selected birthdays. 0296 */ 0297 void BirthdayDlg::slotOk() 0298 { 0299 // Save prefix and suffix texts to use as future defaults 0300 KConfigGroup config(KSharedConfig::openConfig(), GENERAL_GROUP); 0301 config.writeEntry("BirthdayPrefix", mPrefix->text()); 0302 config.writeEntry("BirthdaySuffix", mSuffix->text()); 0303 config.sync(); 0304 0305 mFlags = KAEvent::ANY_TIME; 0306 if (mSoundPicker->sound() == Preferences::Sound_Beep) mFlags |= KAEvent::BEEP; 0307 if (mSoundPicker->repeatPause() >= 0) mFlags |= KAEvent::REPEAT_SOUND; 0308 if (mConfirmAck->isChecked()) mFlags |= KAEvent::CONFIRM_ACK; 0309 if (mFontColourButton->defaultFont()) mFlags |= KAEvent::DEFAULT_FONT; 0310 QDialog::accept(); 0311 } 0312 0313 /****************************************************************************** 0314 * Called when the group of items selected changes. 0315 * Enable/disable the OK button depending on whether anything is selected. 0316 */ 0317 void BirthdayDlg::slotSelectionChanged() 0318 { 0319 mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(mListView->selectionModel()->hasSelection()); 0320 } 0321 0322 /****************************************************************************** 0323 * Called when the font/color button has been clicked. 0324 * Set the colors in the message text entry control. 0325 */ 0326 void BirthdayDlg::setColours(const QColor& fgColour, const QColor& bgColour) 0327 { 0328 QPalette pal = mPrefix->palette(); 0329 pal.setColor(mPrefix->backgroundRole(), bgColour); 0330 pal.setColor(mPrefix->foregroundRole(), fgColour); 0331 mPrefix->setPalette(pal); 0332 mSuffix->setPalette(pal); 0333 } 0334 0335 /****************************************************************************** 0336 * Called when the data has changed in the birthday list. 0337 * Resize the date column. 0338 */ 0339 void BirthdayDlg::resizeViewColumns() 0340 { 0341 mListView->resizeColumnToContents(mBirthdayModel_DateColumn); 0342 } 0343 0344 /****************************************************************************** 0345 * Called when the prefix or suffix text has lost keyboard focus. 0346 * If the text has changed, re-evaluates the selection list according to the new 0347 * birthday alarm text format. 0348 */ 0349 void BirthdayDlg::slotTextLostFocus() 0350 { 0351 const QString prefix = mPrefix->text(); 0352 const QString suffix = mSuffix->text(); 0353 if (prefix != mPrefixText || suffix != mSuffixText) 0354 { 0355 // Text has changed - re-evaluate the selection list 0356 mPrefixText = prefix; 0357 mSuffixText = suffix; 0358 setSortModelSelectionList(); 0359 } 0360 } 0361 0362 /****************************************************************************** 0363 * Re-evaluates the selection list according to the birthday alarm text format. 0364 */ 0365 void BirthdayDlg::setSortModelSelectionList() 0366 { 0367 AkonadiPlugin* akonadiPlugin = Preferences::akonadiPlugin(); 0368 if (!akonadiPlugin) 0369 return; 0370 0371 QStringList alarmMessageList; 0372 const QList<KAEvent> activeEvents = ResourcesCalendar::events(CalEvent::ACTIVE); 0373 for (const KAEvent& event : activeEvents) 0374 { 0375 if (event.actionSubType() == KAEvent::SubAction::Message 0376 && event.recurType() == KARecurrence::ANNUAL_DATE 0377 && (mPrefixText.isEmpty() || event.message().startsWith(mPrefixText))) 0378 alarmMessageList.append(event.message()); 0379 } 0380 akonadiPlugin->setPrefixSuffix(mBirthdaySortModel, mPrefixText, mSuffixText, alarmMessageList); 0381 } 0382 0383 #include "moc_birthdaydlg.cpp" 0384 0385 // vim: et sw=4: