Factor out the shared parts of the agent action manager setup.
[kdepim.git] / kalarm / sounddlg.cpp
blobb230d621ecf8f49ede468edf93e99668b0c61643
1 /*
2 * sounddlg.cpp - sound file selection and configuration dialog and widget
3 * Program: kalarm
4 * Copyright © 2005-2011 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.moc"
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 <klocale.h>
34 #include <kstandarddirs.h>
35 #include <kiconloader.h>
36 #include <khbox.h>
37 #include <kio/netaccess.h>
38 #include <phonon/mediaobject.h>
39 #include <phonon/audiooutput.h>
40 #include <kdebug.h>
42 #include <QLabel>
43 #include <QDir>
44 #include <QGroupBox>
45 #include <QApplication>
46 #include <QVBoxLayout>
47 #include <QGridLayout>
48 #include <QShowEvent>
49 #include <QResizeEvent>
52 // Collect these widget labels together to ensure consistent wording and
53 // translations across different modules.
54 QString SoundWidget::i18n_chk_Repeat() { return i18nc("@option:check", "Repeat"); }
56 static const char SOUND_DIALOG_NAME[] = "SoundDialog";
59 SoundDlg::SoundDlg(const QString& file, float volume, float fadeVolume, int fadeSeconds, int repeatPause,
60 const QString& caption, QWidget* parent)
61 : KDialog(parent),
62 mReadOnly(false)
64 mSoundWidget = new SoundWidget(true, true, this);
65 setMainWidget(mSoundWidget);
66 setCaption(caption);
67 setButtons(Ok|Cancel);
68 setDefaultButton(Ok);
70 // Restore the dialog size from last time
71 QSize s;
72 if (KAlarm::readConfigWindowSize(SOUND_DIALOG_NAME, s))
73 resize(s);
75 // Initialise the control values
76 mSoundWidget->set(file, volume, fadeVolume, fadeSeconds, repeatPause);
79 /******************************************************************************
80 * Set the read-only status of the dialog.
82 void SoundDlg::setReadOnly(bool readOnly)
84 if (readOnly != mReadOnly)
86 mSoundWidget->setReadOnly(readOnly);
87 mReadOnly = readOnly;
88 if (readOnly)
90 setButtons(Cancel);
91 setDefaultButton(Cancel);
93 else
95 setButtons(Ok|Cancel);
96 setDefaultButton(Ok);
101 KUrl SoundDlg::getFile() const
103 KUrl url;
104 mSoundWidget->file(url);
105 return url;
108 /******************************************************************************
109 * Called when the dialog's size has changed.
110 * Records the new size in the config file.
112 void SoundDlg::resizeEvent(QResizeEvent* re)
114 if (isVisible())
115 KAlarm::writeConfigWindowSize(SOUND_DIALOG_NAME, re->size());
116 KDialog::resizeEvent(re);
119 /******************************************************************************
120 * Called when the OK or Cancel button is clicked.
122 void SoundDlg::slotButtonClicked(int button)
124 if (button == Ok)
126 if (mReadOnly)
127 reject();
128 else if (mSoundWidget->validate(true))
129 accept();
131 else
132 KDialog::slotButtonClicked(button);
136 /*=============================================================================
137 = Class SoundWidget
138 = Select a sound file and configure how to play it.
139 =============================================================================*/
141 SoundWidget::SoundWidget(bool showPlay, bool showRepeat, QWidget* parent)
142 : QWidget(parent),
143 mFilePlay(0),
144 mRepeatGroupBox(0),
145 mRepeatPause(0),
146 mPlayer(0),
147 mReadOnly(false),
148 mEmptyFileAllowed(false)
150 QVBoxLayout* layout = new QVBoxLayout(this);
151 layout->setMargin(0);
152 layout->setSpacing(KDialog::spacingHint());
154 QLabel* label = 0;
155 if (!showPlay)
157 label = new QLabel(i18nc("@label", "Sound file:"), this);
158 layout->addWidget(label);
161 KHBox* box = new KHBox(this);
162 box->setMargin(0);
163 layout->addWidget(box);
165 if (showPlay)
167 // File play button
168 mFilePlay = new QPushButton(box);
169 mFilePlay->setIcon(SmallIcon("media-playback-start"));
170 connect(mFilePlay, SIGNAL(clicked()), SLOT(playSound()));
171 mFilePlay->setToolTip(i18nc("@info:tooltip", "Test the sound"));
172 mFilePlay->setWhatsThis(i18nc("@info:whatsthis", "Play the selected sound file."));
175 // File name edit box
176 mFileEdit = new LineEdit(LineEdit::Url, box);
177 mFileEdit->setAcceptDrops(true);
178 mFileEdit->setWhatsThis(i18nc("@info:whatsthis", "Enter the name or URL of a sound file to play."));
179 if (label)
180 label->setBuddy(mFileEdit);
181 connect(mFileEdit, SIGNAL(textChanged(QString)), SIGNAL(changed()));
183 // File browse button
184 mFileBrowseButton = new PushButton(box);
185 mFileBrowseButton->setIcon(KIcon(SmallIcon("document-open")));
186 int size = mFileBrowseButton->sizeHint().height();
187 mFileBrowseButton->setFixedSize(size, size);
188 connect(mFileBrowseButton, SIGNAL(clicked()), SLOT(slotPickFile()));
189 mFileBrowseButton->setToolTip(i18nc("@info:tooltip", "Choose a file"));
190 mFileBrowseButton->setWhatsThis(i18nc("@info:whatsthis", "Select a sound file to play."));
192 if (mFilePlay)
194 int size = qMax(mFilePlay->sizeHint().height(), mFileBrowseButton->sizeHint().height());
195 mFilePlay->setFixedSize(size, size);
196 mFileBrowseButton->setFixedSize(size, size);
199 if (showRepeat)
201 // Sound repetition checkbox
202 mRepeatGroupBox = new GroupBox(i18n_chk_Repeat(), this);
203 mRepeatGroupBox->setCheckable(true);
204 mRepeatGroupBox->setWhatsThis(i18nc("@info:whatsthis", "If checked, the sound file will be played repeatedly for as long as the message is displayed."));
205 connect(mRepeatGroupBox, SIGNAL(toggled(bool)), SIGNAL(changed()));
206 layout->addWidget(mRepeatGroupBox);
207 QVBoxLayout* glayout = new QVBoxLayout(mRepeatGroupBox);
209 // Pause between repetitions
210 KHBox* box = new KHBox(mRepeatGroupBox);
211 box->setMargin(0);
212 box->setSpacing(KDialog::spacingHint());
213 glayout->addWidget(box);
214 label = new QLabel(i18nc("@label:spinbox Length of time to pause between repetitions", "Pause between repetitions:"), box);
215 label->setFixedSize(label->sizeHint());
216 mRepeatPause = new SpinBox(0, 999, box);
217 mRepeatPause->setSingleShiftStep(10);
218 mRepeatPause->setFixedSize(mRepeatPause->sizeHint());
219 label->setBuddy(mRepeatPause);
220 connect(mRepeatPause, SIGNAL(valueChanged(int)), SIGNAL(changed()));
221 label = new QLabel(i18nc("@label", "seconds"), box);
222 label->setFixedSize(label->sizeHint());
223 box->setWhatsThis(i18nc("@info:whatsthis", "Enter how many seconds to pause between repetitions."));
226 // Volume
227 QGroupBox* group = new QGroupBox(i18nc("@title:group Sound volume", "Volume"), this);
228 layout->addWidget(group);
229 QGridLayout* grid = new QGridLayout(group);
230 grid->setMargin(KDialog::marginHint());
231 grid->setSpacing(KDialog::spacingHint());
232 grid->setColumnStretch(2, 1);
233 int indentWidth = 3 * KDialog::spacingHint();
234 grid->setColumnMinimumWidth(0, indentWidth);
235 grid->setColumnMinimumWidth(1, indentWidth);
237 // 'Set volume' checkbox
238 box = new KHBox(group);
239 box->setMargin(0);
240 box->setSpacing(KDialog::spacingHint());
241 grid->addWidget(box, 1, 0, 1, 3);
242 mVolumeCheckbox = new CheckBox(i18nc("@option:check", "Set volume"), box);
243 mVolumeCheckbox->setFixedSize(mVolumeCheckbox->sizeHint());
244 connect(mVolumeCheckbox, SIGNAL(toggled(bool)), SLOT(slotVolumeToggled(bool)));
245 mVolumeCheckbox->setWhatsThis(i18nc("@info:whatsthis", "Select to choose the volume for playing the sound file."));
247 // Volume slider
248 mVolumeSlider = new Slider(0, 100, 10, Qt::Horizontal, box);
249 mVolumeSlider->setTickPosition(QSlider::TicksBelow);
250 mVolumeSlider->setTickInterval(10);
251 mVolumeSlider->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
252 mVolumeSlider->setWhatsThis(i18nc("@info:whatsthis", "Choose the volume for playing the sound file."));
253 mVolumeCheckbox->setFocusWidget(mVolumeSlider);
254 connect(mVolumeSlider, SIGNAL(valueChanged(int)), SIGNAL(changed()));
256 // Fade checkbox
257 mFadeCheckbox = new CheckBox(i18nc("@option:check", "Fade"), group);
258 mFadeCheckbox->setFixedSize(mFadeCheckbox->sizeHint());
259 connect(mFadeCheckbox, SIGNAL(toggled(bool)), SLOT(slotFadeToggled(bool)));
260 mFadeCheckbox->setWhatsThis(i18nc("@info:whatsthis", "Select to fade the volume when the sound file first starts to play."));
261 grid->addWidget(mFadeCheckbox, 2, 1, 1, 2, Qt::AlignLeft);
263 // Fade time
264 mFadeBox = new KHBox(group);
265 mFadeBox->setMargin(0);
266 mFadeBox->setSpacing(KDialog::spacingHint());
267 grid->addWidget(mFadeBox, 3, 2, Qt::AlignLeft);
268 label = new QLabel(i18nc("@label:spinbox Time period over which to fade the sound", "Fade time:"), mFadeBox);
269 label->setFixedSize(label->sizeHint());
270 mFadeTime = new SpinBox(1, 999, mFadeBox);
271 mFadeTime->setSingleShiftStep(10);
272 mFadeTime->setFixedSize(mFadeTime->sizeHint());
273 label->setBuddy(mFadeTime);
274 connect(mFadeTime, SIGNAL(valueChanged(int)), SIGNAL(changed()));
275 label = new QLabel(i18nc("@label", "seconds"), mFadeBox);
276 label->setFixedSize(label->sizeHint());
277 mFadeBox->setWhatsThis(i18nc("@info:whatsthis", "Enter how many seconds to fade the sound before reaching the set volume."));
279 // Fade slider
280 mFadeVolumeBox = new KHBox(group);
281 mFadeVolumeBox->setMargin(0);
282 mFadeVolumeBox->setSpacing(KDialog::spacingHint());
283 grid->addWidget(mFadeVolumeBox, 4, 2);
284 label = new QLabel(i18nc("@label:slider", "Initial volume:"), mFadeVolumeBox);
285 label->setFixedSize(label->sizeHint());
286 mFadeSlider = new Slider(0, 100, 10, Qt::Horizontal, mFadeVolumeBox);
287 mFadeSlider->setTickPosition(QSlider::TicksBelow);
288 mFadeSlider->setTickInterval(10);
289 mFadeSlider->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
290 label->setBuddy(mFadeSlider);
291 connect(mFadeSlider, SIGNAL(valueChanged(int)), SIGNAL(changed()));
292 mFadeVolumeBox->setWhatsThis(i18nc("@info:whatsthis", "Choose the initial volume for playing the sound file."));
294 slotVolumeToggled(false);
297 SoundWidget::~SoundWidget()
299 delete mPlayer; // this stops playing if not already stopped
300 mPlayer = 0;
303 /******************************************************************************
304 * Set the controls' values.
306 void SoundWidget::set(const QString& file, float volume, float fadeVolume, int fadeSeconds, int repeatPause)
308 // Initialise the control values
309 mFileEdit->setText(KAlarm::pathOrUrl(file));
310 if (mRepeatGroupBox)
312 mRepeatGroupBox->setChecked(repeatPause >= 0);
313 mRepeatPause->setValue(repeatPause >= 0 ? repeatPause : 0);
315 mVolumeCheckbox->setChecked(volume >= 0);
316 mVolumeSlider->setValue(volume >= 0 ? static_cast<int>(volume*100) : 100);
317 mFadeCheckbox->setChecked(fadeVolume >= 0);
318 mFadeSlider->setValue(fadeVolume >= 0 ? static_cast<int>(fadeVolume*100) : 100);
319 mFadeTime->setValue(fadeSeconds);
320 slotVolumeToggled(volume >= 0);
323 /******************************************************************************
324 * Set the read-only status of the widget.
326 void SoundWidget::setReadOnly(bool readOnly)
328 if (readOnly != mReadOnly)
330 mFileEdit->setReadOnly(readOnly);
331 mFileBrowseButton->setReadOnly(readOnly);
332 if (mRepeatGroupBox)
333 mRepeatGroupBox->setReadOnly(readOnly);
334 mVolumeCheckbox->setReadOnly(readOnly);
335 mVolumeSlider->setReadOnly(readOnly);
336 mFadeCheckbox->setReadOnly(readOnly);
337 mFadeTime->setReadOnly(readOnly);
338 mFadeSlider->setReadOnly(readOnly);
339 mReadOnly = readOnly;
343 /******************************************************************************
344 * Return the file name typed in the edit field.
346 QString SoundWidget::fileName() const
348 return mFileEdit->text();
351 /******************************************************************************
352 * Validate the entered file and return it.
354 bool SoundWidget::file(KUrl& url, bool showErrorMessage) const
356 bool result = validate(showErrorMessage);
357 url = mUrl;
358 return result;
361 /******************************************************************************
362 * Return the entered repetition and volume settings:
363 * 'volume' is in range 0 - 1, or < 0 if volume is not to be set.
364 * 'fadeVolume is similar, with 'fadeTime' set to the fade interval in seconds.
366 void SoundWidget::getVolume(float& volume, float& fadeVolume, int& fadeSeconds) const
368 volume = mVolumeCheckbox->isChecked() ? (float)mVolumeSlider->value() / 100 : -1;
369 if (mFadeCheckbox->isChecked())
371 fadeVolume = (float)mFadeSlider->value() / 100;
372 fadeSeconds = mFadeTime->value();
374 else
376 fadeVolume = -1;
377 fadeSeconds = 0;
381 /******************************************************************************
382 * Return the entered repetition setting.
383 * Reply = seconds to pause between repetitions, or -1 if no repeat.
385 int SoundWidget::repeatPause() const
387 return mRepeatGroupBox && mRepeatGroupBox->isChecked() ? mRepeatPause->value() : -1;
390 /******************************************************************************
391 * Called when the dialog's size has changed.
392 * Records the new size in the config file.
394 void SoundWidget::resizeEvent(QResizeEvent* re)
396 mVolumeSlider->resize(mFadeSlider->size());
397 QWidget::resizeEvent(re);
400 void SoundWidget::showEvent(QShowEvent* se)
402 mVolumeSlider->resize(mFadeSlider->size());
403 QWidget::showEvent(se);
406 /******************************************************************************
407 * Called when the file browser button is clicked.
409 void SoundWidget::slotPickFile()
411 QString url = SoundPicker::browseFile(mDefaultDir, mFileEdit->text());
412 if (!url.isEmpty())
413 mFileEdit->setText(KAlarm::pathOrUrl(url));
416 /******************************************************************************
417 * Called when the file play or stop button is clicked.
419 void SoundWidget::playSound()
421 if (mPlayer)
423 // The file is currently playing. Stop it.
424 playFinished();
425 return;
427 if (!validate(true))
428 return;
429 #if 0
430 #warning Phonon::createPlayer() does not work
431 mPlayer = Phonon::createPlayer(Phonon::MusicCategory, mUrl);
432 mPlayer->setParent(this);
433 #else
434 mPlayer = new Phonon::MediaObject(this);
435 Phonon::AudioOutput* output = new Phonon::AudioOutput(Phonon::MusicCategory, mPlayer);
436 mPlayer->setCurrentSource(mUrl);
437 Phonon::createPath(mPlayer, output);
438 #endif
439 connect(mPlayer, SIGNAL(finished()), SLOT(playFinished()));
440 mFilePlay->setIcon(SmallIcon("media-playback-stop")); // change the play button to a stop button
441 mFilePlay->setToolTip(i18nc("@info:tooltip", "Stop sound"));
442 mFilePlay->setWhatsThis(i18nc("@info:whatsthis", "Stop playing the sound"));
443 mPlayer->play();
446 /******************************************************************************
447 * Called when playing the file has completed, or to stop playing.
449 void SoundWidget::playFinished()
451 delete mPlayer; // this stops playing if not already stopped
452 mPlayer = 0;
453 mFilePlay->setIcon(SmallIcon("media-playback-start"));
454 mFilePlay->setToolTip(i18nc("@info:tooltip", "Test the sound"));
455 mFilePlay->setWhatsThis(i18nc("@info:whatsthis", "Play the selected sound file."));
458 /******************************************************************************
459 * Check whether the specified sound file exists.
461 bool SoundWidget::validate(bool showErrorMessage) const
463 QString file = mFileEdit->text();
464 if (file == mValidatedFile && !file.isEmpty())
465 return true;
466 mValidatedFile = file;
467 if (file.isEmpty() && mEmptyFileAllowed)
469 mUrl.clear();
470 return true;
472 KAlarm::FileErr err = KAlarm::checkFileExists(file, mUrl);
473 if (err == KAlarm::FileErr_None)
474 return true;
475 if (err == KAlarm::FileErr_Nonexistent)
477 mUrl = KUrl(file);
478 if (mUrl.isLocalFile() && !file.startsWith(QLatin1String("/")))
480 // It's a relative path.
481 // Find the first sound resource that contains files.
482 QStringList soundDirs = KGlobal::dirs()->resourceDirs("sound");
483 if (!soundDirs.isEmpty())
485 QDir dir;
486 dir.setFilter(QDir::Files | QDir::Readable);
487 for (int i = 0, end = soundDirs.count(); i < end; ++i)
489 dir = soundDirs[i];
490 if (dir.isReadable() && dir.count() > 2)
492 mUrl.setPath(soundDirs[i]);
493 mUrl.addPath(file);
494 QString f = mUrl.toLocalFile();
495 err = KAlarm::checkFileExists(f, mUrl);
496 if (err == KAlarm::FileErr_None)
497 return true;
498 if (err != KAlarm::FileErr_Nonexistent)
500 file = f; // for inclusion in error message
501 break;
506 if (err == KAlarm::FileErr_Nonexistent)
508 mUrl.setPath(QDir::homePath());
509 mUrl.addPath(file);
510 QString f = mUrl.toLocalFile();
511 err = KAlarm::checkFileExists(f, mUrl);
512 if (err == KAlarm::FileErr_None)
513 return true;
514 if (err != KAlarm::FileErr_Nonexistent)
515 file = f; // for inclusion in error message
519 mFileEdit->setFocus();
520 if (showErrorMessage
521 && KAlarm::showFileErrMessage(file, err, KAlarm::FileErr_BlankPlay, const_cast<SoundWidget*>(this)))
522 return true;
523 mValidatedFile.clear();
524 mUrl.clear();
525 return false;
528 /******************************************************************************
529 * Called when the Set Volume checkbox is toggled.
531 void SoundWidget::slotVolumeToggled(bool on)
533 mVolumeSlider->setEnabled(on);
534 mFadeCheckbox->setEnabled(on);
535 slotFadeToggled(on && mFadeCheckbox->isChecked());
538 /******************************************************************************
539 * Called when the Fade checkbox is toggled.
541 void SoundWidget::slotFadeToggled(bool on)
543 mFadeBox->setEnabled(on);
544 mFadeVolumeBox->setEnabled(on);
545 emit changed();
548 // vim: et sw=4: