Rename method
[kdepim.git] / kalarm / sounddlg.cpp
blobf99b9b671733b33f0b997c73cd08a4cd67171bfb
1 /*
2 * sounddlg.cpp - sound file selection and configuration dialog and widget
3 * Program: kalarm
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.
21 #include "kalarm.h"
22 #include "sounddlg.h"
24 #include "checkbox.h"
25 #include "functions.h"
26 #include "groupbox.h"
27 #include "lineedit.h"
28 #include "pushbutton.h"
29 #include "slider.h"
30 #include "soundpicker.h"
31 #include "spinbox.h"
33 #include <QIcon>
34 #include <KGlobal>
35 #include <KLocalizedString>
36 #include <kstandarddirs.h>
37 #include <kiconloader.h>
38 #include <phonon/mediaobject.h>
39 #include <phonon/audiooutput.h>
41 #include <QLabel>
42 #include <QDir>
43 #include <QGroupBox>
44 #include <QHBoxLayout>
45 #include <QVBoxLayout>
46 #include <QGridLayout>
47 #include <QShowEvent>
48 #include <QResizeEvent>
49 #include <QDialogButtonBox>
50 #include <QStyle>
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)
63 : QDialog(parent),
64 mReadOnly(false)
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
80 QSize s;
81 if (KAlarm::readConfigWindowSize(SOUND_DIALOG_NAME, s))
82 resize(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);
96 mReadOnly = readOnly;
97 if (readOnly)
99 mButtonBox->clear();
100 mButtonBox->addButton(QDialogButtonBox::Cancel);
102 else
104 mButtonBox->clear();
105 mButtonBox->addButton(QDialogButtonBox::Ok);
106 mButtonBox->addButton(QDialogButtonBox::Cancel);
111 QUrl SoundDlg::getFile() const
113 QUrl url;
114 mSoundWidget->file(url);
115 return 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)
124 if (isVisible())
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))
136 if (mReadOnly)
137 reject();
138 else if (mSoundWidget->validate(true))
139 accept();
141 else
142 reject();
147 /*=============================================================================
148 = Class SoundWidget
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)
154 : QWidget(parent),
155 mFilePlay(Q_NULLPTR),
156 mRepeatGroupBox(Q_NULLPTR),
157 mRepeatPause(Q_NULLPTR),
158 mPlayer(Q_NULLPTR),
159 mReadOnly(false),
160 mEmptyFileAllowed(false)
162 QVBoxLayout* layout = new QVBoxLayout(this);
164 QLabel* label = Q_NULLPTR;
165 if (!showPlay)
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);
177 if (showPlay)
179 // File play button
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."));
193 if (label)
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."));
207 if (mFilePlay)
209 int size = qMax(mFilePlay->sizeHint().height(), mFileBrowseButton->sizeHint().height());
210 mFilePlay->setFixedSize(size, size);
211 mFileBrowseButton->setFixedSize(size, size);
214 if (showRepeat)
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."));
244 // Volume
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."));
266 // Volume slider
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);
276 // Fade checkbox
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);
283 // Fade time
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."));
302 // Fade slider
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
325 mPlayer = Q_NULLPTR;
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));
335 if (mRepeatGroupBox)
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);
357 if (mRepeatGroupBox)
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);
382 url = mUrl;
383 return result;
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();
399 else
401 fadeVolume = -1;
402 fadeSeconds = 0;
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());
437 if (!url.isEmpty())
438 mFileEdit->setText(KAlarm::pathOrUrl(url));
441 /******************************************************************************
442 * Called when the file play or stop button is clicked.
444 void SoundWidget::playSound()
446 if (mPlayer)
448 // The file is currently playing. Stop it.
449 playFinished();
450 return;
452 if (!validate(true))
453 return;
454 #if 0
455 #warning Phonon::createPlayer() does not work
456 mPlayer = Phonon::createPlayer(Phonon::MusicCategory, mUrl);
457 mPlayer->setParent(this);
458 #else
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);
463 #endif
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"));
468 mPlayer->play();
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
477 mPlayer = Q_NULLPTR;
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())
490 return true;
491 mValidatedFile = file;
492 if (file.isEmpty() && mEmptyFileAllowed)
494 mUrl.clear();
495 return true;
497 KAlarm::FileErr err = KAlarm::checkFileExists(file, mUrl);
498 if (err == KAlarm::FileErr_None)
499 return true;
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())
510 QDir dir;
511 dir.setFilter(QDir::Files | QDir::Readable);
512 for (int i = 0, end = soundDirs.count(); i < end; ++i)
514 dir = soundDirs[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)
521 return true;
522 if (err != KAlarm::FileErr_Nonexistent)
524 file = f; // for inclusion in error message
525 break;
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)
536 return true;
537 if (err != KAlarm::FileErr_Nonexistent)
538 file = f; // for inclusion in error message
542 mFileEdit->setFocus();
543 if (showErrorMessage
544 && KAlarm::showFileErrMessage(file, err, KAlarm::FileErr_BlankPlay, const_cast<SoundWidget*>(this)))
545 return true;
546 mValidatedFile.clear();
547 mUrl.clear();
548 return false;
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);
568 Q_EMIT changed();
571 // vim: et sw=4: