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 <phonon/mediaobject.h>
38 #include <phonon/audiooutput.h>
43 #include <QHBoxLayout>
44 #include <QVBoxLayout>
45 #include <QGridLayout>
47 #include <QResizeEvent>
48 #include <QDialogButtonBox>
50 #include "kalarm_debug.h"
53 // Collect these widget labels together to ensure consistent wording and
54 // translations across different modules.
55 QString
SoundWidget::i18n_chk_Repeat() { return i18nc("@option:check", "Repeat"); }
57 static const char SOUND_DIALOG_NAME
[] = "SoundDialog";
60 SoundDlg::SoundDlg(const QString
& file
, float volume
, float fadeVolume
, int fadeSeconds
, int repeatPause
,
61 const QString
& caption
, QWidget
* parent
)
65 QVBoxLayout
*layout
= new QVBoxLayout(this);
66 mSoundWidget
= new SoundWidget(true, true, this);
67 layout
->addWidget(mSoundWidget
);
69 mButtonBox
= new QDialogButtonBox
;
70 mButtonBox
->addButton(QDialogButtonBox::Ok
);
71 mButtonBox
->addButton(QDialogButtonBox::Cancel
);
72 connect(mButtonBox
, &QDialogButtonBox::clicked
,
73 this, &SoundDlg::slotButtonClicked
);
74 layout
->addWidget(mButtonBox
);
76 setWindowTitle(caption
);
78 // Restore the dialog size from last time
80 if (KAlarm::readConfigWindowSize(SOUND_DIALOG_NAME
, s
))
83 // Initialise the control values
84 mSoundWidget
->set(file
, volume
, fadeVolume
, fadeSeconds
, repeatPause
);
87 /******************************************************************************
88 * Set the read-only status of the dialog.
90 void SoundDlg::setReadOnly(bool readOnly
)
92 if (readOnly
!= mReadOnly
)
94 mSoundWidget
->setReadOnly(readOnly
);
99 mButtonBox
->addButton(QDialogButtonBox::Cancel
);
104 mButtonBox
->addButton(QDialogButtonBox::Ok
);
105 mButtonBox
->addButton(QDialogButtonBox::Cancel
);
110 QUrl
SoundDlg::getFile() const
113 mSoundWidget
->file(url
);
117 /******************************************************************************
118 * Called when the dialog's size has changed.
119 * Records the new size in the config file.
121 void SoundDlg::resizeEvent(QResizeEvent
* re
)
124 KAlarm::writeConfigWindowSize(SOUND_DIALOG_NAME
, re
->size());
125 QDialog::resizeEvent(re
);
128 /******************************************************************************
129 * Called when the OK or Cancel button is clicked.
131 void SoundDlg::slotButtonClicked(QAbstractButton
*button
)
133 if (button
== mButtonBox
->button(QDialogButtonBox::Ok
))
137 else if (mSoundWidget
->validate(true))
146 /*=============================================================================
148 = Select a sound file and configure how to play it.
149 =============================================================================*/
150 QString
SoundWidget::mDefaultDir
;
152 SoundWidget::SoundWidget(bool showPlay
, bool showRepeat
, QWidget
* parent
)
154 mFilePlay(Q_NULLPTR
),
155 mRepeatGroupBox(Q_NULLPTR
),
156 mRepeatPause(Q_NULLPTR
),
159 mEmptyFileAllowed(false)
161 QVBoxLayout
* layout
= new QVBoxLayout(this);
163 QLabel
* label
= Q_NULLPTR
;
166 label
= new QLabel(i18nc("@label", "Sound file:"), this);
167 layout
->addWidget(label
);
170 QWidget
* box
= new QWidget(this);
171 layout
->addWidget(box
);
172 QHBoxLayout
* boxHLayout
= new QHBoxLayout(box
);
173 boxHLayout
->setMargin(0);
174 boxHLayout
->setSpacing(0);
179 mFilePlay
= new QPushButton(box
);
180 boxHLayout
->addWidget(mFilePlay
);
181 mFilePlay
->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
182 connect(mFilePlay
, &QPushButton::clicked
, this, &SoundWidget::playSound
);
183 mFilePlay
->setToolTip(i18nc("@info:tooltip", "Test the sound"));
184 mFilePlay
->setWhatsThis(i18nc("@info:whatsthis", "Play the selected sound file."));
187 // File name edit box
188 mFileEdit
= new LineEdit(LineEdit::Url
, box
);
189 boxHLayout
->addWidget(mFileEdit
);
190 mFileEdit
->setAcceptDrops(true);
191 mFileEdit
->setWhatsThis(i18nc("@info:whatsthis", "Enter the name or URL of a sound file to play."));
193 label
->setBuddy(mFileEdit
);
194 connect(mFileEdit
, &LineEdit::textChanged
, this, &SoundWidget::changed
);
196 // File browse button
197 mFileBrowseButton
= new PushButton(box
);
198 boxHLayout
->addWidget(mFileBrowseButton
);
199 mFileBrowseButton
->setIcon(QIcon(QIcon::fromTheme(QStringLiteral("document-open"))));
200 int size
= mFileBrowseButton
->sizeHint().height();
201 mFileBrowseButton
->setFixedSize(size
, size
);
202 connect(mFileBrowseButton
, &PushButton::clicked
, this, &SoundWidget::slotPickFile
);
203 mFileBrowseButton
->setToolTip(i18nc("@info:tooltip", "Choose a file"));
204 mFileBrowseButton
->setWhatsThis(i18nc("@info:whatsthis", "Select a sound file to play."));
208 int size
= qMax(mFilePlay
->sizeHint().height(), mFileBrowseButton
->sizeHint().height());
209 mFilePlay
->setFixedSize(size
, size
);
210 mFileBrowseButton
->setFixedSize(size
, size
);
215 // Sound repetition checkbox
216 mRepeatGroupBox
= new GroupBox(i18n_chk_Repeat(), this);
217 mRepeatGroupBox
->setCheckable(true);
218 mRepeatGroupBox
->setWhatsThis(i18nc("@info:whatsthis", "If checked, the sound file will be played repeatedly for as long as the message is displayed."));
219 connect(mRepeatGroupBox
, &GroupBox::toggled
, this, &SoundWidget::changed
);
220 layout
->addWidget(mRepeatGroupBox
);
221 QVBoxLayout
* glayout
= new QVBoxLayout(mRepeatGroupBox
);
223 // Pause between repetitions
224 QWidget
* box
= new QWidget(mRepeatGroupBox
);
225 glayout
->addWidget(box
);
226 boxHLayout
= new QHBoxLayout(box
);
227 boxHLayout
->setMargin(0);
228 label
= new QLabel(i18nc("@label:spinbox Length of time to pause between repetitions", "Pause between repetitions:"), box
);
229 boxHLayout
->addWidget(label
);
230 label
->setFixedSize(label
->sizeHint());
231 mRepeatPause
= new SpinBox(0, 999, box
);
232 boxHLayout
->addWidget(mRepeatPause
);
233 mRepeatPause
->setSingleShiftStep(10);
234 mRepeatPause
->setFixedSize(mRepeatPause
->sizeHint());
235 label
->setBuddy(mRepeatPause
);
236 connect(mRepeatPause
, static_cast<void (SpinBox::*)(int)>(&SpinBox::valueChanged
), this, &SoundWidget::changed
);
237 label
= new QLabel(i18nc("@label", "seconds"), box
);
238 boxHLayout
->addWidget(label
);
239 label
->setFixedSize(label
->sizeHint());
240 box
->setWhatsThis(i18nc("@info:whatsthis", "Enter how many seconds to pause between repetitions."));
244 QGroupBox
* group
= new QGroupBox(i18nc("@title:group Sound volume", "Volume"), this);
245 layout
->addWidget(group
);
246 QGridLayout
* grid
= new QGridLayout(group
);
247 grid
->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin
));
248 grid
->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
249 grid
->setColumnStretch(2, 1);
250 int indentWidth
= 3 * style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
);
251 grid
->setColumnMinimumWidth(0, indentWidth
);
252 grid
->setColumnMinimumWidth(1, indentWidth
);
254 // 'Set volume' checkbox
255 box
= new QWidget(group
);
256 grid
->addWidget(box
, 1, 0, 1, 3);
257 boxHLayout
= new QHBoxLayout(box
);
258 boxHLayout
->setMargin(0);
259 mVolumeCheckbox
= new CheckBox(i18nc("@option:check", "Set volume"), box
);
260 boxHLayout
->addWidget(mVolumeCheckbox
);
261 mVolumeCheckbox
->setFixedSize(mVolumeCheckbox
->sizeHint());
262 connect(mVolumeCheckbox
, &CheckBox::toggled
, this, &SoundWidget::slotVolumeToggled
);
263 mVolumeCheckbox
->setWhatsThis(i18nc("@info:whatsthis", "Select to choose the volume for playing the sound file."));
266 mVolumeSlider
= new Slider(0, 100, 10, Qt::Horizontal
, box
);
267 boxHLayout
->addWidget(mVolumeSlider
);
268 mVolumeSlider
->setTickPosition(QSlider::TicksBelow
);
269 mVolumeSlider
->setTickInterval(10);
270 mVolumeSlider
->setSizePolicy(QSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Fixed
));
271 mVolumeSlider
->setWhatsThis(i18nc("@info:whatsthis", "Choose the volume for playing the sound file."));
272 mVolumeCheckbox
->setFocusWidget(mVolumeSlider
);
273 connect(mVolumeSlider
, &Slider::valueChanged
, this, &SoundWidget::changed
);
276 mFadeCheckbox
= new CheckBox(i18nc("@option:check", "Fade"), group
);
277 mFadeCheckbox
->setFixedSize(mFadeCheckbox
->sizeHint());
278 connect(mFadeCheckbox
, &CheckBox::toggled
, this, &SoundWidget::slotFadeToggled
);
279 mFadeCheckbox
->setWhatsThis(i18nc("@info:whatsthis", "Select to fade the volume when the sound file first starts to play."));
280 grid
->addWidget(mFadeCheckbox
, 2, 1, 1, 2, Qt::AlignLeft
);
283 mFadeBox
= new QWidget(group
);
284 grid
->addWidget(mFadeBox
, 3, 2, Qt::AlignLeft
);
285 boxHLayout
= new QHBoxLayout(mFadeBox
);
286 boxHLayout
->setMargin(0);
287 label
= new QLabel(i18nc("@label:spinbox Time period over which to fade the sound", "Fade time:"), mFadeBox
);
288 boxHLayout
->addWidget(label
);
289 label
->setFixedSize(label
->sizeHint());
290 mFadeTime
= new SpinBox(1, 999, mFadeBox
);
291 boxHLayout
->addWidget(mFadeTime
);
292 mFadeTime
->setSingleShiftStep(10);
293 mFadeTime
->setFixedSize(mFadeTime
->sizeHint());
294 label
->setBuddy(mFadeTime
);
295 connect(mFadeTime
, static_cast<void (SpinBox::*)(int)>(&SpinBox::valueChanged
), this, &SoundWidget::changed
);
296 label
= new QLabel(i18nc("@label", "seconds"), mFadeBox
);
297 boxHLayout
->addWidget(label
);
298 label
->setFixedSize(label
->sizeHint());
299 mFadeBox
->setWhatsThis(i18nc("@info:whatsthis", "Enter how many seconds to fade the sound before reaching the set volume."));
302 mFadeVolumeBox
= new QWidget(group
);
303 grid
->addWidget(mFadeVolumeBox
, 4, 2);
304 boxHLayout
= new QHBoxLayout(mFadeVolumeBox
);
305 boxHLayout
->setMargin(0);
306 label
= new QLabel(i18nc("@label:slider", "Initial volume:"), mFadeVolumeBox
);
307 boxHLayout
->addWidget(label
);
308 label
->setFixedSize(label
->sizeHint());
309 mFadeSlider
= new Slider(0, 100, 10, Qt::Horizontal
, mFadeVolumeBox
);
310 boxHLayout
->addWidget(mFadeSlider
);
311 mFadeSlider
->setTickPosition(QSlider::TicksBelow
);
312 mFadeSlider
->setTickInterval(10);
313 mFadeSlider
->setSizePolicy(QSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Fixed
));
314 label
->setBuddy(mFadeSlider
);
315 connect(mFadeSlider
, &Slider::valueChanged
, this, &SoundWidget::changed
);
316 mFadeVolumeBox
->setWhatsThis(i18nc("@info:whatsthis", "Choose the initial volume for playing the sound file."));
318 slotVolumeToggled(false);
321 SoundWidget::~SoundWidget()
323 delete mPlayer
; // this stops playing if not already stopped
327 /******************************************************************************
328 * Set the controls' values.
330 void SoundWidget::set(const QString
& file
, float volume
, float fadeVolume
, int fadeSeconds
, int repeatPause
)
332 // Initialise the control values
333 mFileEdit
->setText(KAlarm::pathOrUrl(file
));
336 mRepeatGroupBox
->setChecked(repeatPause
>= 0);
337 mRepeatPause
->setValue(repeatPause
>= 0 ? repeatPause
: 0);
339 mVolumeCheckbox
->setChecked(volume
>= 0);
340 mVolumeSlider
->setValue(volume
>= 0 ? static_cast<int>(volume
*100) : 100);
341 mFadeCheckbox
->setChecked(fadeVolume
>= 0);
342 mFadeSlider
->setValue(fadeVolume
>= 0 ? static_cast<int>(fadeVolume
*100) : 100);
343 mFadeTime
->setValue(fadeSeconds
);
344 slotVolumeToggled(volume
>= 0);
347 /******************************************************************************
348 * Set the read-only status of the widget.
350 void SoundWidget::setReadOnly(bool readOnly
)
352 if (readOnly
!= mReadOnly
)
354 mFileEdit
->setReadOnly(readOnly
);
355 mFileBrowseButton
->setReadOnly(readOnly
);
357 mRepeatGroupBox
->setReadOnly(readOnly
);
358 mVolumeCheckbox
->setReadOnly(readOnly
);
359 mVolumeSlider
->setReadOnly(readOnly
);
360 mFadeCheckbox
->setReadOnly(readOnly
);
361 mFadeTime
->setReadOnly(readOnly
);
362 mFadeSlider
->setReadOnly(readOnly
);
363 mReadOnly
= readOnly
;
367 /******************************************************************************
368 * Return the file name typed in the edit field.
370 QString
SoundWidget::fileName() const
372 return mFileEdit
->text();
375 /******************************************************************************
376 * Validate the entered file and return it.
378 bool SoundWidget::file(QUrl
& url
, bool showErrorMessage
) const
380 bool result
= validate(showErrorMessage
);
385 /******************************************************************************
386 * Return the entered repetition and volume settings:
387 * 'volume' is in range 0 - 1, or < 0 if volume is not to be set.
388 * 'fadeVolume is similar, with 'fadeTime' set to the fade interval in seconds.
390 void SoundWidget::getVolume(float& volume
, float& fadeVolume
, int& fadeSeconds
) const
392 volume
= mVolumeCheckbox
->isChecked() ? (float)mVolumeSlider
->value() / 100 : -1;
393 if (mFadeCheckbox
->isChecked())
395 fadeVolume
= (float)mFadeSlider
->value() / 100;
396 fadeSeconds
= mFadeTime
->value();
405 /******************************************************************************
406 * Return the entered repetition setting.
407 * Reply = seconds to pause between repetitions, or -1 if no repeat.
409 int SoundWidget::repeatPause() const
411 return mRepeatGroupBox
&& mRepeatGroupBox
->isChecked() ? mRepeatPause
->value() : -1;
414 /******************************************************************************
415 * Called when the dialog's size has changed.
416 * Records the new size in the config file.
418 void SoundWidget::resizeEvent(QResizeEvent
* re
)
420 mVolumeSlider
->resize(mFadeSlider
->size());
421 QWidget::resizeEvent(re
);
424 void SoundWidget::showEvent(QShowEvent
* se
)
426 mVolumeSlider
->resize(mFadeSlider
->size());
427 QWidget::showEvent(se
);
430 /******************************************************************************
431 * Called when the file browser button is clicked.
433 void SoundWidget::slotPickFile()
435 QString url
= SoundPicker::browseFile(mDefaultDir
, mFileEdit
->text());
437 mFileEdit
->setText(KAlarm::pathOrUrl(url
));
440 /******************************************************************************
441 * Called when the file play or stop button is clicked.
443 void SoundWidget::playSound()
447 // The file is currently playing. Stop it.
454 #warning Phonon::createPlayer() does not work
455 mPlayer
= Phonon::createPlayer(Phonon::MusicCategory
, mUrl
);
456 mPlayer
->setParent(this);
458 mPlayer
= new Phonon::MediaObject(this);
459 Phonon::AudioOutput
* output
= new Phonon::AudioOutput(Phonon::MusicCategory
, mPlayer
);
460 mPlayer
->setCurrentSource(mUrl
);
461 Phonon::createPath(mPlayer
, output
);
463 connect(mPlayer
, &Phonon::MediaObject::finished
, this, &SoundWidget::playFinished
);
464 mFilePlay
->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-stop"))); // change the play button to a stop button
465 mFilePlay
->setToolTip(i18nc("@info:tooltip", "Stop sound"));
466 mFilePlay
->setWhatsThis(i18nc("@info:whatsthis", "Stop playing the sound"));
470 /******************************************************************************
471 * Called when playing the file has completed, or to stop playing.
473 void SoundWidget::playFinished()
475 delete mPlayer
; // this stops playing if not already stopped
477 mFilePlay
->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
478 mFilePlay
->setToolTip(i18nc("@info:tooltip", "Test the sound"));
479 mFilePlay
->setWhatsThis(i18nc("@info:whatsthis", "Play the selected sound file."));
482 /******************************************************************************
483 * Check whether the specified sound file exists.
485 bool SoundWidget::validate(bool showErrorMessage
) const
487 QString file
= mFileEdit
->text();
488 if (file
== mValidatedFile
&& !file
.isEmpty())
490 mValidatedFile
= file
;
491 if (file
.isEmpty() && mEmptyFileAllowed
)
496 KAlarm::FileErr err
= KAlarm::checkFileExists(file
, mUrl
);
497 if (err
== KAlarm::FileErr_None
)
499 if (err
== KAlarm::FileErr_Nonexistent
)
501 if (mUrl
.isLocalFile() && !file
.startsWith(QStringLiteral("/")))
503 // It's a relative path.
504 // Find the first sound resource that contains files.
505 QStringList soundDirs
= KGlobal::dirs()->resourceDirs("sound");
506 if (!soundDirs
.isEmpty())
509 dir
.setFilter(QDir::Files
| QDir::Readable
);
510 for (int i
= 0, end
= soundDirs
.count(); i
< end
; ++i
)
513 if (dir
.isReadable() && dir
.count() > 2)
515 QString f
= soundDirs
[i
] + QDir::separator() + file
;
516 err
= KAlarm::checkFileExists(f
, mUrl
);
517 if (err
== KAlarm::FileErr_None
)
519 if (err
!= KAlarm::FileErr_Nonexistent
)
521 file
= f
; // for inclusion in error message
527 if (err
== KAlarm::FileErr_Nonexistent
)
529 QString f
= QDir::homePath() + QDir::separator() + file
;
530 err
= KAlarm::checkFileExists(f
, mUrl
);
531 if (err
== KAlarm::FileErr_None
)
533 if (err
!= KAlarm::FileErr_Nonexistent
)
534 file
= f
; // for inclusion in error message
538 mFileEdit
->setFocus();
540 && KAlarm::showFileErrMessage(file
, err
, KAlarm::FileErr_BlankPlay
, const_cast<SoundWidget
*>(this)))
542 mValidatedFile
.clear();
547 /******************************************************************************
548 * Called when the Set Volume checkbox is toggled.
550 void SoundWidget::slotVolumeToggled(bool on
)
552 mVolumeSlider
->setEnabled(on
);
553 mFadeCheckbox
->setEnabled(on
);
554 slotFadeToggled(on
&& mFadeCheckbox
->isChecked());
557 /******************************************************************************
558 * Called when the Fade checkbox is toggled.
560 void SoundWidget::slotFadeToggled(bool on
)
562 mFadeBox
->setEnabled(on
);
563 mFadeVolumeBox
->setEnabled(on
);