SVN_SILENT made messages (.desktop file) - always resolve ours
[kdepim.git] / kalarm / soundpicker.cpp
blob1faaea99d3c128c3700c4ef74094695dfc7e9764
1 /*
2 * soundpicker.cpp - widget to select a sound file or a beep
3 * Program: kalarm
4 * Copyright © 2002-2013 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 "kalarm.h"
23 #include "autoqpointer.h"
24 #include "combobox.h"
25 #include "functions.h"
26 #include "kalarmapp.h"
27 #include "pushbutton.h"
28 #include "sounddlg.h"
29 #include "soundpicker.h"
31 #include <kpimtextedit/texttospeech.h>
33 #include <QIcon>
34 #include <KLocalizedString>
35 #include <phonon/backendcapabilities.h>
37 #include <QTimer>
38 #include <QLabel>
39 #include <QHBoxLayout>
40 #include <QStandardPaths>
41 #include "kalarm_debug.h"
44 static QMap<Preferences::SoundType, int> indexes; // mapping from sound type to combo index
47 // Collect these widget labels together to ensure consistent wording and
48 // translations across different modules.
49 QString SoundPicker::i18n_label_Sound() { return i18nc("@label:listbox Listbox providing audio options", "Sound:"); }
50 QString SoundPicker::i18n_combo_None() { return i18nc("@item:inlistbox No sound", "None"); }
51 QString SoundPicker::i18n_combo_Beep() { return i18nc("@item:inlistbox", "Beep"); }
52 QString SoundPicker::i18n_combo_Speak() { return i18nc("@item:inlistbox", "Speak"); }
53 QString SoundPicker::i18n_combo_File() { return i18nc("@item:inlistbox", "Sound file"); }
56 SoundPicker::SoundPicker(QWidget* parent)
57 : QFrame(parent),
58 mRevertType(false),
59 mReadOnly(false)
61 QHBoxLayout* soundLayout = new QHBoxLayout(this);
62 soundLayout->setMargin(0);
63 soundLayout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
64 mTypeBox = new QWidget(this); // this is to control the QWhatsThis text display area
65 QHBoxLayout* typeBoxLayout = new QHBoxLayout(mTypeBox);
66 typeBoxLayout->setMargin(0);
68 QLabel* label = new QLabel(i18n_label_Sound(), mTypeBox);
69 typeBoxLayout->addWidget(label);
70 label->setFixedSize(label->sizeHint());
72 // Sound type combo box
73 // The order of combo box entries must correspond with the 'Type' enum.
74 if (indexes.isEmpty())
76 indexes[Preferences::Sound_None] = 0;
77 indexes[Preferences::Sound_Beep] = 1;
78 indexes[Preferences::Sound_File] = 2;
79 indexes[Preferences::Sound_Speak] = 3;
82 mTypeCombo = new ComboBox(mTypeBox);
83 typeBoxLayout->addWidget(mTypeCombo);
84 mTypeCombo->addItem(i18n_combo_None()); // index None
85 mTypeCombo->addItem(i18n_combo_Beep()); // index Beep
86 mTypeCombo->addItem(i18n_combo_File()); // index PlayFile
87 mSpeakShowing = !KPIMTextEdit::TextToSpeech::self()->isReady();
88 showSpeak(!mSpeakShowing); // index Speak (only displayed if appropriate)
89 connect(mTypeCombo, static_cast<void (ComboBox::*)(int)>(&ComboBox::activated), this, &SoundPicker::slotTypeSelected);
90 connect(mTypeCombo, static_cast<void (ComboBox::*)(int)>(&ComboBox::currentIndexChanged), this, &SoundPicker::changed);
91 label->setBuddy(mTypeCombo);
92 soundLayout->addWidget(mTypeBox);
94 // Sound file picker button
95 mFilePicker = new PushButton(this);
96 mFilePicker->setIcon(QIcon::fromTheme(QStringLiteral("audio-x-generic")));
97 int size = mFilePicker->sizeHint().height();
98 mFilePicker->setFixedSize(size, size);
99 connect(mFilePicker, &PushButton::clicked, this, &SoundPicker::slotPickFile);
100 mFilePicker->setToolTip(i18nc("@info:tooltip", "Configure sound file"));
101 mFilePicker->setWhatsThis(i18nc("@info:whatsthis", "Configure a sound file to play when the alarm is displayed."));
102 soundLayout->addWidget(mFilePicker);
104 // Initialise the file picker button state and tooltip
105 mTypeCombo->setCurrentIndex(indexes[Preferences::Sound_None]);
106 mFilePicker->setEnabled(false);
109 /******************************************************************************
110 * Set the read-only status of the widget.
112 void SoundPicker::setReadOnly(bool readOnly)
114 // Don't set the sound file picker read-only since it still needs to
115 // display the read-only SoundDlg.
116 mTypeCombo->setReadOnly(readOnly);
117 mReadOnly = readOnly;
120 /******************************************************************************
121 * Show or hide the Speak option.
123 void SoundPicker::showSpeak(bool show)
125 if (!KPIMTextEdit::TextToSpeech::self()->isReady())
126 show = false; // speech capability is not installed
127 if (show == mSpeakShowing)
128 return; // no change
129 if (!show && mTypeCombo->currentIndex() == indexes[Preferences::Sound_Speak])
130 mTypeCombo->setCurrentIndex(indexes[Preferences::Sound_None]);
131 if (mTypeCombo->count() == indexes[Preferences::Sound_Speak]+1)
132 mTypeCombo->removeItem(indexes[Preferences::Sound_Speak]); // precaution in case of mix-ups
133 QString whatsThis;
134 QString opt1 = xi18nc("@info:whatsthis", "<interface>%1</interface>: the message is displayed silently.", i18n_combo_None());
135 QString opt2 = xi18nc("@info:whatsthis", "<interface>%1</interface>: a simple beep is sounded.", i18n_combo_Beep());
136 QString opt3 = xi18nc("@info:whatsthis", "<interface>%1</interface>: an audio file is played. You will be prompted to choose the file and set play options.", i18n_combo_File());
137 if (show)
139 mTypeCombo->addItem(i18n_combo_Speak());
140 QString opt4 = xi18nc("@info:whatsthis", "<interface>%1</interface>: the message text is spoken.", i18n_combo_Speak());
141 whatsThis = xi18nc("@info:whatsthis Combination of multiple whatsthis items",
142 "<para>Choose a sound to play when the message is displayed:"
143 "<list><item>%1</item>"
144 "<item>%2</item>"
145 "<item>%3</item>"
146 "<item>%4</item></list></para>", opt1, opt2, opt3, opt4);
148 else
149 whatsThis = xi18nc("@info:whatsthis Combination of multiple whatsthis items",
150 "<para>Choose a sound to play when the message is displayed:"
151 "<list><item>%1</item>"
152 "<item>%2</item>"
153 "<item>%3</item></list></para>", opt1, opt2, opt3);
154 mTypeBox->setWhatsThis(whatsThis);
155 mSpeakShowing = show;
158 /******************************************************************************
159 * Return the currently selected option.
161 Preferences::SoundType SoundPicker::sound() const
163 int current = mTypeCombo->currentIndex();
164 for (QMap<Preferences::SoundType, int>::ConstIterator it = indexes.constBegin(); it != indexes.constEnd(); ++it)
165 if (it.value() == current)
166 return it.key();
167 return Preferences::Sound_None;
170 /******************************************************************************
171 * Return the selected sound file, if the File option is selected.
172 * Returns empty URL if File is not currently selected.
174 QUrl SoundPicker::file() const
176 return (mTypeCombo->currentIndex() == indexes[Preferences::Sound_File]) ? mFile : QUrl();
179 /******************************************************************************
180 * Return the specified volumes (range 0 - 1).
181 * Returns < 0 if beep is currently selected, or if 'set volume' is not selected.
183 float SoundPicker::volume(float& fadeVolume, int& fadeSeconds) const
185 if (mTypeCombo->currentIndex() == indexes[Preferences::Sound_File] && !mFile.isEmpty())
187 fadeVolume = mFadeVolume;
188 fadeSeconds = mFadeSeconds;
189 return mVolume;
191 else
193 fadeVolume = -1;
194 fadeSeconds = 0;
195 return -1;
199 /******************************************************************************
200 * Return the pause between sound file repetitions is selected.
201 * Reply = pause in seconds, or -1 if repetition is not selected or beep is selected.
203 int SoundPicker::repeatPause() const
205 return mTypeCombo->currentIndex() == indexes[Preferences::Sound_File] && !mFile.isEmpty() ? mRepeatPause : -1;
208 /******************************************************************************
209 * Initialise the widget's state.
211 void SoundPicker::set(Preferences::SoundType type, const QString& f, float volume, float fadeVolume, int fadeSeconds, int repeatPause)
213 if (type == Preferences::Sound_File && f.isEmpty())
214 type = Preferences::Sound_Beep;
215 mFile = QUrl::fromUserInput(f, QString(), QUrl::AssumeLocalFile);
216 mVolume = volume;
217 mFadeVolume = fadeVolume;
218 mFadeSeconds = fadeSeconds;
219 mRepeatPause = repeatPause;
220 mTypeCombo->setCurrentIndex(indexes[type]); // this doesn't trigger slotTypeSelected()
221 mFilePicker->setEnabled(type == Preferences::Sound_File);
222 mTypeCombo->setToolTip(type == Preferences::Sound_File ? mFile.toDisplayString() : QString());
223 mLastType = type;
226 /******************************************************************************
227 * Called when the sound option is changed.
229 void SoundPicker::slotTypeSelected(int id)
231 Preferences::SoundType newType = Preferences::Sound_None;
232 for (QMap<Preferences::SoundType, int>::ConstIterator it = indexes.constBegin(); it != indexes.constEnd(); ++it)
234 if (it.value() == id)
236 newType = it.key();
237 break;
240 if (newType == mLastType || mRevertType)
241 return;
242 if (mLastType == Preferences::Sound_File)
244 mFilePicker->setEnabled(false);
245 mTypeCombo->setToolTip(QString());
247 else if (newType == Preferences::Sound_File)
249 if (mFile.isEmpty())
251 slotPickFile();
252 if (mFile.isEmpty())
253 return; // revert to previously selected type
255 mFilePicker->setEnabled(true);
256 mTypeCombo->setToolTip(mFile.toDisplayString());
258 mLastType = newType;
261 /******************************************************************************
262 * Called when the file picker button is clicked.
264 void SoundPicker::slotPickFile()
266 QUrl oldfile = mFile;
267 // Use AutoQPointer to guard against crash on application exit while
268 // the dialogue is still open. It prevents double deletion (both on
269 // deletion of EditAlarmDlg, and on return from this function).
270 AutoQPointer<SoundDlg> dlg = new SoundDlg(mFile.toDisplayString(), mVolume, mFadeVolume, mFadeSeconds, mRepeatPause, i18nc("@title:window", "Sound File"), this);
271 dlg->setReadOnly(mReadOnly);
272 bool accepted = (dlg->exec() == QDialog::Accepted);
273 if (mReadOnly)
274 return;
275 if (accepted)
277 float volume, fadeVolume;
278 int fadeTime;
279 dlg->getVolume(volume, fadeVolume, fadeTime);
280 QUrl file = dlg->getFile();
281 if (!file.isEmpty())
282 mFile = file;
283 mRepeatPause = dlg->repeatPause();
284 mVolume = volume;
285 mFadeVolume = fadeVolume;
286 mFadeSeconds = fadeTime;
288 if (mFile.isEmpty())
290 // No audio file is selected, so revert to previously selected option
291 #if 0
292 // Remove mRevertType, setLastType(), #include QTimer
293 // But wait a moment until setting the radio button, or it won't work.
294 mRevertType = true; // prevent sound dialog popping up twice
295 QTimer::singleShot(0, this, SLOT(setLastType()));
296 #else
297 mTypeCombo->setCurrentIndex(indexes[mLastType]);
298 #endif
299 mTypeCombo->setToolTip(QString());
301 else
302 mTypeCombo->setToolTip(mFile.toDisplayString());
303 if (accepted || mFile != oldfile)
304 Q_EMIT changed();
307 /******************************************************************************
308 * Select the previously selected sound type.
310 void SoundPicker::setLastType()
312 mTypeCombo->setCurrentIndex(indexes[mLastType]);
313 mRevertType = false;
316 /******************************************************************************
317 * Display a dialog to choose a sound file, initially highlighting any
318 * specified file. 'initialFile' must be a full path name or URL.
319 * 'defaultDir' is updated to the directory containing the chosen file.
320 * Reply = URL selected. If none is selected, URL.isEmpty() is true.
322 QString SoundPicker::browseFile(QString& defaultDir, const QString& initialFile)
324 static QString kdeSoundDir; // directory containing KDE sound files
325 if (defaultDir.isEmpty())
327 if (kdeSoundDir.isNull())
329 kdeSoundDir = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("sound/KDE-Sys-Warning.ogg"));
330 kdeSoundDir = QFileInfo(kdeSoundDir).absolutePath();
332 defaultDir = kdeSoundDir;
334 QString filter = Phonon::BackendCapabilities::availableMimeTypes().join(QStringLiteral(" "));
335 return KAlarm::browseFile(i18nc("@title:window", "Choose Sound File"), defaultDir, initialFile, filter, KFile::ExistingOnly, Q_NULLPTR);
340 // vim: et sw=4: