2 * soundpicker.cpp - widget to select a sound file or a beep
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.
23 #include "autoqpointer.h"
25 #include "functions.h"
26 #include "kalarmapp.h"
27 #include "pushbutton.h"
29 #include "soundpicker.h"
31 #include <kpimtextedit/texttospeech.h>
34 #include <KLocalizedString>
35 #include <phonon/backendcapabilities.h>
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
)
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
)
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
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());
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>"
146 "<item>%4</item></list></para>", opt1
, opt2
, opt3
, opt4
);
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>"
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
)
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
;
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
);
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());
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
)
240 if (newType
== mLastType
|| mRevertType
)
242 if (mLastType
== Preferences::Sound_File
)
244 mFilePicker
->setEnabled(false);
245 mTypeCombo
->setToolTip(QString());
247 else if (newType
== Preferences::Sound_File
)
253 return; // revert to previously selected type
255 mFilePicker
->setEnabled(true);
256 mTypeCombo
->setToolTip(mFile
.toDisplayString());
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
);
277 float volume
, fadeVolume
;
279 dlg
->getVolume(volume
, fadeVolume
, fadeTime
);
280 QUrl file
= dlg
->getFile();
283 mRepeatPause
= dlg
->repeatPause();
285 mFadeVolume
= fadeVolume
;
286 mFadeSeconds
= fadeTime
;
290 // No audio file is selected, so revert to previously selected option
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()));
297 mTypeCombo
->setCurrentIndex(indexes
[mLastType
]);
299 mTypeCombo
->setToolTip(QString());
302 mTypeCombo
->setToolTip(mFile
.toDisplayString());
303 if (accepted
|| mFile
!= oldfile
)
307 /******************************************************************************
308 * Select the previously selected sound type.
310 void SoundPicker::setLastType()
312 mTypeCombo
->setCurrentIndex(indexes
[mLastType
]);
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
);