2 * sounddlg.cpp - sound file selection and configuration dialog and widget
4 * Copyright © 2005-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.
25 #include "functions.h"
28 #include "pushbutton.h"
30 #include "soundpicker.h"
35 #include <KLocalizedString>
36 #include <kstandarddirs.h>
37 #include <kiconloader.h>
38 #include <phonon/mediaobject.h>
39 #include <phonon/audiooutput.h>
44 #include <QHBoxLayout>
45 #include <QVBoxLayout>
46 #include <QGridLayout>
48 #include <QResizeEvent>
49 #include <QDialogButtonBox>
51 #include "kalarm_debug.h"
54 // Collect these widget labels together to ensure consistent wording and
55 // translations across different modules.
56 QString
SoundWidget::i18n_chk_Repeat() { return i18nc("@option:check", "Repeat"); }
58 static const char SOUND_DIALOG_NAME
[] = "SoundDialog";
61 SoundDlg::SoundDlg(const QString
& file
, float volume
, float fadeVolume
, int fadeSeconds
, int repeatPause
,
62 const QString
& caption
, QWidget
* parent
)
66 QVBoxLayout
*layout
= new QVBoxLayout(this);
67 mSoundWidget
= new SoundWidget(true, true, this);
68 layout
->addWidget(mSoundWidget
);
70 mButtonBox
= new QDialogButtonBox
;
71 mButtonBox
->addButton(QDialogButtonBox::Ok
);
72 mButtonBox
->addButton(QDialogButtonBox::Cancel
);
73 connect(mButtonBox
, &QDialogButtonBox::clicked
,
74 this, &SoundDlg::slotButtonClicked
);
75 layout
->addWidget(mButtonBox
);
77 setWindowTitle(caption
);
79 // Restore the dialog size from last time
81 if (KAlarm::readConfigWindowSize(SOUND_DIALOG_NAME
, s
))
84 // Initialise the control values
85 mSoundWidget
->set(file
, volume
, fadeVolume
, fadeSeconds
, repeatPause
);
88 /******************************************************************************
89 * Set the read-only status of the dialog.
91 void SoundDlg::setReadOnly(bool readOnly
)
93 if (readOnly
!= mReadOnly
)
95 mSoundWidget
->setReadOnly(readOnly
);
100 mButtonBox
->addButton(QDialogButtonBox::Cancel
);
105 mButtonBox
->addButton(QDialogButtonBox::Ok
);
106 mButtonBox
->addButton(QDialogButtonBox::Cancel
);
111 QUrl
SoundDlg::getFile() const
114 mSoundWidget
->file(url
);
118 /******************************************************************************
119 * Called when the dialog's size has changed.
120 * Records the new size in the config file.
122 void SoundDlg::resizeEvent(QResizeEvent
* re
)
125 KAlarm::writeConfigWindowSize(SOUND_DIALOG_NAME
, re
->size());
126 QDialog::resizeEvent(re
);
129 /******************************************************************************
130 * Called when the OK or Cancel button is clicked.
132 void SoundDlg::slotButtonClicked(QAbstractButton
*button
)
134 if (button
== mButtonBox
->button(QDialogButtonBox::Ok
))
138 else if (mSoundWidget
->validate(true))
147 /*=============================================================================
149 = Select a sound file and configure how to play it.
150 =============================================================================*/
151 QString
SoundWidget::mDefaultDir
;
153 SoundWidget::SoundWidget(bool showPlay
, bool showRepeat
, QWidget
* parent
)
155 mFilePlay(Q_NULLPTR
),
156 mRepeatGroupBox(Q_NULLPTR
),
157 mRepeatPause(Q_NULLPTR
),
160 mEmptyFileAllowed(false)
162 QVBoxLayout
* layout
= new QVBoxLayout(this);
164 QLabel
* label
= Q_NULLPTR
;
167 label
= new QLabel(i18nc("@label", "Sound file:"), this);
168 layout
->addWidget(label
);
171 QWidget
* box
= new QWidget(this);
172 layout
->addWidget(box
);
173 QHBoxLayout
* boxHLayout
= new QHBoxLayout(box
);
174 boxHLayout
->setMargin(0);
175 boxHLayout
->setSpacing(0);
180 mFilePlay
= new QPushButton(box
);
181 boxHLayout
->addWidget(mFilePlay
);
182 mFilePlay
->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
183 connect(mFilePlay
, &QPushButton::clicked
, this, &SoundWidget::playSound
);
184 mFilePlay
->setToolTip(i18nc("@info:tooltip", "Test the sound"));
185 mFilePlay
->setWhatsThis(i18nc("@info:whatsthis", "Play the selected sound file."));
188 // File name edit box
189 mFileEdit
= new LineEdit(LineEdit::Url
, box
);
190 boxHLayout
->addWidget(mFileEdit
);
191 mFileEdit
->setAcceptDrops(true);
192 mFileEdit
->setWhatsThis(i18nc("@info:whatsthis", "Enter the name or URL of a sound file to play."));
194 label
->setBuddy(mFileEdit
);
195 connect(mFileEdit
, &LineEdit::textChanged
, this, &SoundWidget::changed
);
197 // File browse button
198 mFileBrowseButton
= new PushButton(box
);
199 boxHLayout
->addWidget(mFileBrowseButton
);
200 mFileBrowseButton
->setIcon(QIcon(QIcon::fromTheme(QStringLiteral("document-open"))));
201 int size
= mFileBrowseButton
->sizeHint().height();
202 mFileBrowseButton
->setFixedSize(size
, size
);
203 connect(mFileBrowseButton
, &PushButton::clicked
, this, &SoundWidget::slotPickFile
);
204 mFileBrowseButton
->setToolTip(i18nc("@info:tooltip", "Choose a file"));
205 mFileBrowseButton
->setWhatsThis(i18nc("@info:whatsthis", "Select a sound file to play."));
209 int size
= qMax(mFilePlay
->sizeHint().height(), mFileBrowseButton
->sizeHint().height());
210 mFilePlay
->setFixedSize(size
, size
);
211 mFileBrowseButton
->setFixedSize(size
, size
);
216 // Sound repetition checkbox
217 mRepeatGroupBox
= new GroupBox(i18n_chk_Repeat(), this);
218 mRepeatGroupBox
->setCheckable(true);
219 mRepeatGroupBox
->setWhatsThis(i18nc("@info:whatsthis", "If checked, the sound file will be played repeatedly for as long as the message is displayed."));
220 connect(mRepeatGroupBox
, &GroupBox::toggled
, this, &SoundWidget::changed
);
221 layout
->addWidget(mRepeatGroupBox
);
222 QVBoxLayout
* glayout
= new QVBoxLayout(mRepeatGroupBox
);
224 // Pause between repetitions
225 QWidget
* box
= new QWidget(mRepeatGroupBox
);
226 glayout
->addWidget(box
);
227 boxHLayout
= new QHBoxLayout(box
);
228 boxHLayout
->setMargin(0);
229 label
= new QLabel(i18nc("@label:spinbox Length of time to pause between repetitions", "Pause between repetitions:"), box
);
230 boxHLayout
->addWidget(label
);
231 label
->setFixedSize(label
->sizeHint());
232 mRepeatPause
= new SpinBox(0, 999, box
);
233 boxHLayout
->addWidget(mRepeatPause
);
234 mRepeatPause
->setSingleShiftStep(10);
235 mRepeatPause
->setFixedSize(mRepeatPause
->sizeHint());
236 label
->setBuddy(mRepeatPause
);
237 connect(mRepeatPause
, static_cast<void (SpinBox::*)(int)>(&SpinBox::valueChanged
), this, &SoundWidget::changed
);
238 label
= new QLabel(i18nc("@label", "seconds"), box
);
239 boxHLayout
->addWidget(label
);
240 label
->setFixedSize(label
->sizeHint());
241 box
->setWhatsThis(i18nc("@info:whatsthis", "Enter how many seconds to pause between repetitions."));
245 QGroupBox
* group
= new QGroupBox(i18nc("@title:group Sound volume", "Volume"), this);
246 layout
->addWidget(group
);
247 QGridLayout
* grid
= new QGridLayout(group
);
248 grid
->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin
));
249 grid
->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
250 grid
->setColumnStretch(2, 1);
251 int indentWidth
= 3 * style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
);
252 grid
->setColumnMinimumWidth(0, indentWidth
);
253 grid
->setColumnMinimumWidth(1, indentWidth
);
255 // 'Set volume' checkbox
256 box
= new QWidget(group
);
257 grid
->addWidget(box
, 1, 0, 1, 3);
258 boxHLayout
= new QHBoxLayout(box
);
259 boxHLayout
->setMargin(0);
260 mVolumeCheckbox
= new CheckBox(i18nc("@option:check", "Set volume"), box
);
261 boxHLayout
->addWidget(mVolumeCheckbox
);
262 mVolumeCheckbox
->setFixedSize(mVolumeCheckbox
->sizeHint());
263 connect(mVolumeCheckbox
, &CheckBox::toggled
, this, &SoundWidget::slotVolumeToggled
);
264 mVolumeCheckbox
->setWhatsThis(i18nc("@info:whatsthis", "Select to choose the volume for playing the sound file."));
267 mVolumeSlider
= new Slider(0, 100, 10, Qt::Horizontal
, box
);
268 boxHLayout
->addWidget(mVolumeSlider
);
269 mVolumeSlider
->setTickPosition(QSlider::TicksBelow
);
270 mVolumeSlider
->setTickInterval(10);
271 mVolumeSlider
->setSizePolicy(QSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Fixed
));
272 mVolumeSlider
->setWhatsThis(i18nc("@info:whatsthis", "Choose the volume for playing the sound file."));
273 mVolumeCheckbox
->setFocusWidget(mVolumeSlider
);
274 connect(mVolumeSlider
, &Slider::valueChanged
, this, &SoundWidget::changed
);
277 mFadeCheckbox
= new CheckBox(i18nc("@option:check", "Fade"), group
);
278 mFadeCheckbox
->setFixedSize(mFadeCheckbox
->sizeHint());
279 connect(mFadeCheckbox
, &CheckBox::toggled
, this, &SoundWidget::slotFadeToggled
);
280 mFadeCheckbox
->setWhatsThis(i18nc("@info:whatsthis", "Select to fade the volume when the sound file first starts to play."));
281 grid
->addWidget(mFadeCheckbox
, 2, 1, 1, 2, Qt::AlignLeft
);
284 mFadeBox
= new QWidget(group
);
285 grid
->addWidget(mFadeBox
, 3, 2, Qt::AlignLeft
);
286 boxHLayout
= new QHBoxLayout(mFadeBox
);
287 boxHLayout
->setMargin(0);
288 label
= new QLabel(i18nc("@label:spinbox Time period over which to fade the sound", "Fade time:"), mFadeBox
);
289 boxHLayout
->addWidget(label
);
290 label
->setFixedSize(label
->sizeHint());
291 mFadeTime
= new SpinBox(1, 999, mFadeBox
);
292 boxHLayout
->addWidget(mFadeTime
);
293 mFadeTime
->setSingleShiftStep(10);
294 mFadeTime
->setFixedSize(mFadeTime
->sizeHint());
295 label
->setBuddy(mFadeTime
);
296 connect(mFadeTime
, static_cast<void (SpinBox::*)(int)>(&SpinBox::valueChanged
), this, &SoundWidget::changed
);
297 label
= new QLabel(i18nc("@label", "seconds"), mFadeBox
);
298 boxHLayout
->addWidget(label
);
299 label
->setFixedSize(label
->sizeHint());
300 mFadeBox
->setWhatsThis(i18nc("@info:whatsthis", "Enter how many seconds to fade the sound before reaching the set volume."));
303 mFadeVolumeBox
= new QWidget(group
);
304 grid
->addWidget(mFadeVolumeBox
, 4, 2);
305 boxHLayout
= new QHBoxLayout(mFadeVolumeBox
);
306 boxHLayout
->setMargin(0);
307 label
= new QLabel(i18nc("@label:slider", "Initial volume:"), mFadeVolumeBox
);
308 boxHLayout
->addWidget(label
);
309 label
->setFixedSize(label
->sizeHint());
310 mFadeSlider
= new Slider(0, 100, 10, Qt::Horizontal
, mFadeVolumeBox
);
311 boxHLayout
->addWidget(mFadeSlider
);
312 mFadeSlider
->setTickPosition(QSlider::TicksBelow
);
313 mFadeSlider
->setTickInterval(10);
314 mFadeSlider
->setSizePolicy(QSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Fixed
));
315 label
->setBuddy(mFadeSlider
);
316 connect(mFadeSlider
, &Slider::valueChanged
, this, &SoundWidget::changed
);
317 mFadeVolumeBox
->setWhatsThis(i18nc("@info:whatsthis", "Choose the initial volume for playing the sound file."));
319 slotVolumeToggled(false);
322 SoundWidget::~SoundWidget()
324 delete mPlayer
; // this stops playing if not already stopped
328 /******************************************************************************
329 * Set the controls' values.
331 void SoundWidget::set(const QString
& file
, float volume
, float fadeVolume
, int fadeSeconds
, int repeatPause
)
333 // Initialise the control values
334 mFileEdit
->setText(KAlarm::pathOrUrl(file
));
337 mRepeatGroupBox
->setChecked(repeatPause
>= 0);
338 mRepeatPause
->setValue(repeatPause
>= 0 ? repeatPause
: 0);
340 mVolumeCheckbox
->setChecked(volume
>= 0);
341 mVolumeSlider
->setValue(volume
>= 0 ? static_cast<int>(volume
*100) : 100);
342 mFadeCheckbox
->setChecked(fadeVolume
>= 0);
343 mFadeSlider
->setValue(fadeVolume
>= 0 ? static_cast<int>(fadeVolume
*100) : 100);
344 mFadeTime
->setValue(fadeSeconds
);
345 slotVolumeToggled(volume
>= 0);
348 /******************************************************************************
349 * Set the read-only status of the widget.
351 void SoundWidget::setReadOnly(bool readOnly
)
353 if (readOnly
!= mReadOnly
)
355 mFileEdit
->setReadOnly(readOnly
);
356 mFileBrowseButton
->setReadOnly(readOnly
);
358 mRepeatGroupBox
->setReadOnly(readOnly
);
359 mVolumeCheckbox
->setReadOnly(readOnly
);
360 mVolumeSlider
->setReadOnly(readOnly
);
361 mFadeCheckbox
->setReadOnly(readOnly
);
362 mFadeTime
->setReadOnly(readOnly
);
363 mFadeSlider
->setReadOnly(readOnly
);
364 mReadOnly
= readOnly
;
368 /******************************************************************************
369 * Return the file name typed in the edit field.
371 QString
SoundWidget::fileName() const
373 return mFileEdit
->text();
376 /******************************************************************************
377 * Validate the entered file and return it.
379 bool SoundWidget::file(QUrl
& url
, bool showErrorMessage
) const
381 bool result
= validate(showErrorMessage
);
386 /******************************************************************************
387 * Return the entered repetition and volume settings:
388 * 'volume' is in range 0 - 1, or < 0 if volume is not to be set.
389 * 'fadeVolume is similar, with 'fadeTime' set to the fade interval in seconds.
391 void SoundWidget::getVolume(float& volume
, float& fadeVolume
, int& fadeSeconds
) const
393 volume
= mVolumeCheckbox
->isChecked() ? (float)mVolumeSlider
->value() / 100 : -1;
394 if (mFadeCheckbox
->isChecked())
396 fadeVolume
= (float)mFadeSlider
->value() / 100;
397 fadeSeconds
= mFadeTime
->value();
406 /******************************************************************************
407 * Return the entered repetition setting.
408 * Reply = seconds to pause between repetitions, or -1 if no repeat.
410 int SoundWidget::repeatPause() const
412 return mRepeatGroupBox
&& mRepeatGroupBox
->isChecked() ? mRepeatPause
->value() : -1;
415 /******************************************************************************
416 * Called when the dialog's size has changed.
417 * Records the new size in the config file.
419 void SoundWidget::resizeEvent(QResizeEvent
* re
)
421 mVolumeSlider
->resize(mFadeSlider
->size());
422 QWidget::resizeEvent(re
);
425 void SoundWidget::showEvent(QShowEvent
* se
)
427 mVolumeSlider
->resize(mFadeSlider
->size());
428 QWidget::showEvent(se
);
431 /******************************************************************************
432 * Called when the file browser button is clicked.
434 void SoundWidget::slotPickFile()
436 QString url
= SoundPicker::browseFile(mDefaultDir
, mFileEdit
->text());
438 mFileEdit
->setText(KAlarm::pathOrUrl(url
));
441 /******************************************************************************
442 * Called when the file play or stop button is clicked.
444 void SoundWidget::playSound()
448 // The file is currently playing. Stop it.
455 #warning Phonon::createPlayer() does not work
456 mPlayer
= Phonon::createPlayer(Phonon::MusicCategory
, mUrl
);
457 mPlayer
->setParent(this);
459 mPlayer
= new Phonon::MediaObject(this);
460 Phonon::AudioOutput
* output
= new Phonon::AudioOutput(Phonon::MusicCategory
, mPlayer
);
461 mPlayer
->setCurrentSource(mUrl
);
462 Phonon::createPath(mPlayer
, output
);
464 connect(mPlayer
, &Phonon::MediaObject::finished
, this, &SoundWidget::playFinished
);
465 mFilePlay
->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-stop"))); // change the play button to a stop button
466 mFilePlay
->setToolTip(i18nc("@info:tooltip", "Stop sound"));
467 mFilePlay
->setWhatsThis(i18nc("@info:whatsthis", "Stop playing the sound"));
471 /******************************************************************************
472 * Called when playing the file has completed, or to stop playing.
474 void SoundWidget::playFinished()
476 delete mPlayer
; // this stops playing if not already stopped
478 mFilePlay
->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
479 mFilePlay
->setToolTip(i18nc("@info:tooltip", "Test the sound"));
480 mFilePlay
->setWhatsThis(i18nc("@info:whatsthis", "Play the selected sound file."));
483 /******************************************************************************
484 * Check whether the specified sound file exists.
486 bool SoundWidget::validate(bool showErrorMessage
) const
488 QString file
= mFileEdit
->text();
489 if (file
== mValidatedFile
&& !file
.isEmpty())
491 mValidatedFile
= file
;
492 if (file
.isEmpty() && mEmptyFileAllowed
)
497 KAlarm::FileErr err
= KAlarm::checkFileExists(file
, mUrl
);
498 if (err
== KAlarm::FileErr_None
)
500 if (err
== KAlarm::FileErr_Nonexistent
)
502 mUrl
= QUrl::fromUserInput(file
, QString(), QUrl::AssumeLocalFile
);
503 if (mUrl
.isLocalFile() && !file
.startsWith(QStringLiteral("/")))
505 // It's a relative path.
506 // Find the first sound resource that contains files.
507 QStringList soundDirs
= KGlobal::dirs()->resourceDirs("sound");
508 if (!soundDirs
.isEmpty())
511 dir
.setFilter(QDir::Files
| QDir::Readable
);
512 for (int i
= 0, end
= soundDirs
.count(); i
< end
; ++i
)
515 if (dir
.isReadable() && dir
.count() > 2)
517 mUrl
.setPath(soundDirs
[i
] + QDir::separator() + file
);
518 QString f
= mUrl
.toLocalFile();
519 err
= KAlarm::checkFileExists(f
, mUrl
);
520 if (err
== KAlarm::FileErr_None
)
522 if (err
!= KAlarm::FileErr_Nonexistent
)
524 file
= f
; // for inclusion in error message
530 if (err
== KAlarm::FileErr_Nonexistent
)
532 mUrl
.setPath(QDir::homePath() + QDir::separator() + file
);
533 QString f
= mUrl
.toLocalFile();
534 err
= KAlarm::checkFileExists(f
, mUrl
);
535 if (err
== KAlarm::FileErr_None
)
537 if (err
!= KAlarm::FileErr_Nonexistent
)
538 file
= f
; // for inclusion in error message
542 mFileEdit
->setFocus();
544 && KAlarm::showFileErrMessage(file
, err
, KAlarm::FileErr_BlankPlay
, const_cast<SoundWidget
*>(this)))
546 mValidatedFile
.clear();
551 /******************************************************************************
552 * Called when the Set Volume checkbox is toggled.
554 void SoundWidget::slotVolumeToggled(bool on
)
556 mVolumeSlider
->setEnabled(on
);
557 mFadeCheckbox
->setEnabled(on
);
558 slotFadeToggled(on
&& mFadeCheckbox
->isChecked());
561 /******************************************************************************
562 * Called when the Fade checkbox is toggled.
564 void SoundWidget::slotFadeToggled(bool on
)
566 mFadeBox
->setEnabled(on
);
567 mFadeVolumeBox
->setEnabled(on
);