kde-playground/kdepim/kalarm/sounddlg.cpp
2015-04-14 21:49:29 +00:00

548 lines
19 KiB
C++

/*
* sounddlg.cpp - sound file selection and configuration dialog and widget
* Program: kalarm
* Copyright © 2005-2012 by David Jarvie <djarvie@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kalarm.h"
#include "sounddlg.h"
#include "checkbox.h"
#include "functions.h"
#include "groupbox.h"
#include "lineedit.h"
#include "pushbutton.h"
#include "slider.h"
#include "soundpicker.h"
#include "spinbox.h"
#include <klocale.h>
#include <kstandarddirs.h>
#include <kiconloader.h>
#include <khbox.h>
#include <kio/netaccess.h>
#include <phonon/mediaobject.h>
#include <phonon/audiooutput.h>
#include <kdebug.h>
#include <QLabel>
#include <QDir>
#include <QGroupBox>
#include <QVBoxLayout>
#include <QGridLayout>
#include <QShowEvent>
#include <QResizeEvent>
// Collect these widget labels together to ensure consistent wording and
// translations across different modules.
QString SoundWidget::i18n_chk_Repeat() { return i18nc("@option:check", "Repeat"); }
static const char SOUND_DIALOG_NAME[] = "SoundDialog";
SoundDlg::SoundDlg(const QString& file, float volume, float fadeVolume, int fadeSeconds, int repeatPause,
const QString& caption, QWidget* parent)
: KDialog(parent),
mReadOnly(false)
{
mSoundWidget = new SoundWidget(true, true, this);
setMainWidget(mSoundWidget);
setCaption(caption);
setButtons(Ok|Cancel);
setDefaultButton(Ok);
// Restore the dialog size from last time
QSize s;
if (KAlarm::readConfigWindowSize(SOUND_DIALOG_NAME, s))
resize(s);
// Initialise the control values
mSoundWidget->set(file, volume, fadeVolume, fadeSeconds, repeatPause);
}
/******************************************************************************
* Set the read-only status of the dialog.
*/
void SoundDlg::setReadOnly(bool readOnly)
{
if (readOnly != mReadOnly)
{
mSoundWidget->setReadOnly(readOnly);
mReadOnly = readOnly;
if (readOnly)
{
setButtons(Cancel);
setDefaultButton(Cancel);
}
else
{
setButtons(Ok|Cancel);
setDefaultButton(Ok);
}
}
}
KUrl SoundDlg::getFile() const
{
KUrl url;
mSoundWidget->file(url);
return url;
}
/******************************************************************************
* Called when the dialog's size has changed.
* Records the new size in the config file.
*/
void SoundDlg::resizeEvent(QResizeEvent* re)
{
if (isVisible())
KAlarm::writeConfigWindowSize(SOUND_DIALOG_NAME, re->size());
KDialog::resizeEvent(re);
}
/******************************************************************************
* Called when the OK or Cancel button is clicked.
*/
void SoundDlg::slotButtonClicked(int button)
{
if (button == Ok)
{
if (mReadOnly)
reject();
else if (mSoundWidget->validate(true))
accept();
}
else
KDialog::slotButtonClicked(button);
}
/*=============================================================================
= Class SoundWidget
= Select a sound file and configure how to play it.
=============================================================================*/
QString SoundWidget::mDefaultDir;
SoundWidget::SoundWidget(bool showPlay, bool showRepeat, QWidget* parent)
: QWidget(parent),
mFilePlay(0),
mRepeatGroupBox(0),
mRepeatPause(0),
mPlayer(0),
mReadOnly(false),
mEmptyFileAllowed(false)
{
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setMargin(0);
layout->setSpacing(KDialog::spacingHint());
QLabel* label = 0;
if (!showPlay)
{
label = new QLabel(i18nc("@label", "Sound file:"), this);
layout->addWidget(label);
}
KHBox* box = new KHBox(this);
box->setMargin(0);
layout->addWidget(box);
if (showPlay)
{
// File play button
mFilePlay = new QPushButton(box);
mFilePlay->setIcon(SmallIcon(QLatin1String("media-playback-start")));
connect(mFilePlay, SIGNAL(clicked()), SLOT(playSound()));
mFilePlay->setToolTip(i18nc("@info:tooltip", "Test the sound"));
mFilePlay->setWhatsThis(i18nc("@info:whatsthis", "Play the selected sound file."));
}
// File name edit box
mFileEdit = new LineEdit(LineEdit::Url, box);
mFileEdit->setAcceptDrops(true);
mFileEdit->setWhatsThis(i18nc("@info:whatsthis", "Enter the name or URL of a sound file to play."));
if (label)
label->setBuddy(mFileEdit);
connect(mFileEdit, SIGNAL(textChanged(QString)), SIGNAL(changed()));
// File browse button
mFileBrowseButton = new PushButton(box);
mFileBrowseButton->setIcon(KIcon(SmallIcon(QLatin1String("document-open"))));
int size = mFileBrowseButton->sizeHint().height();
mFileBrowseButton->setFixedSize(size, size);
connect(mFileBrowseButton, SIGNAL(clicked()), SLOT(slotPickFile()));
mFileBrowseButton->setToolTip(i18nc("@info:tooltip", "Choose a file"));
mFileBrowseButton->setWhatsThis(i18nc("@info:whatsthis", "Select a sound file to play."));
if (mFilePlay)
{
int size = qMax(mFilePlay->sizeHint().height(), mFileBrowseButton->sizeHint().height());
mFilePlay->setFixedSize(size, size);
mFileBrowseButton->setFixedSize(size, size);
}
if (showRepeat)
{
// Sound repetition checkbox
mRepeatGroupBox = new GroupBox(i18n_chk_Repeat(), this);
mRepeatGroupBox->setCheckable(true);
mRepeatGroupBox->setWhatsThis(i18nc("@info:whatsthis", "If checked, the sound file will be played repeatedly for as long as the message is displayed."));
connect(mRepeatGroupBox, SIGNAL(toggled(bool)), SIGNAL(changed()));
layout->addWidget(mRepeatGroupBox);
QVBoxLayout* glayout = new QVBoxLayout(mRepeatGroupBox);
// Pause between repetitions
KHBox* box = new KHBox(mRepeatGroupBox);
box->setMargin(0);
box->setSpacing(KDialog::spacingHint());
glayout->addWidget(box);
label = new QLabel(i18nc("@label:spinbox Length of time to pause between repetitions", "Pause between repetitions:"), box);
label->setFixedSize(label->sizeHint());
mRepeatPause = new SpinBox(0, 999, box);
mRepeatPause->setSingleShiftStep(10);
mRepeatPause->setFixedSize(mRepeatPause->sizeHint());
label->setBuddy(mRepeatPause);
connect(mRepeatPause, SIGNAL(valueChanged(int)), SIGNAL(changed()));
label = new QLabel(i18nc("@label", "seconds"), box);
label->setFixedSize(label->sizeHint());
box->setWhatsThis(i18nc("@info:whatsthis", "Enter how many seconds to pause between repetitions."));
}
// Volume
QGroupBox* group = new QGroupBox(i18nc("@title:group Sound volume", "Volume"), this);
layout->addWidget(group);
QGridLayout* grid = new QGridLayout(group);
grid->setMargin(KDialog::marginHint());
grid->setSpacing(KDialog::spacingHint());
grid->setColumnStretch(2, 1);
int indentWidth = 3 * KDialog::spacingHint();
grid->setColumnMinimumWidth(0, indentWidth);
grid->setColumnMinimumWidth(1, indentWidth);
// 'Set volume' checkbox
box = new KHBox(group);
box->setMargin(0);
box->setSpacing(KDialog::spacingHint());
grid->addWidget(box, 1, 0, 1, 3);
mVolumeCheckbox = new CheckBox(i18nc("@option:check", "Set volume"), box);
mVolumeCheckbox->setFixedSize(mVolumeCheckbox->sizeHint());
connect(mVolumeCheckbox, SIGNAL(toggled(bool)), SLOT(slotVolumeToggled(bool)));
mVolumeCheckbox->setWhatsThis(i18nc("@info:whatsthis", "Select to choose the volume for playing the sound file."));
// Volume slider
mVolumeSlider = new Slider(0, 100, 10, Qt::Horizontal, box);
mVolumeSlider->setTickPosition(QSlider::TicksBelow);
mVolumeSlider->setTickInterval(10);
mVolumeSlider->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
mVolumeSlider->setWhatsThis(i18nc("@info:whatsthis", "Choose the volume for playing the sound file."));
mVolumeCheckbox->setFocusWidget(mVolumeSlider);
connect(mVolumeSlider, SIGNAL(valueChanged(int)), SIGNAL(changed()));
// Fade checkbox
mFadeCheckbox = new CheckBox(i18nc("@option:check", "Fade"), group);
mFadeCheckbox->setFixedSize(mFadeCheckbox->sizeHint());
connect(mFadeCheckbox, SIGNAL(toggled(bool)), SLOT(slotFadeToggled(bool)));
mFadeCheckbox->setWhatsThis(i18nc("@info:whatsthis", "Select to fade the volume when the sound file first starts to play."));
grid->addWidget(mFadeCheckbox, 2, 1, 1, 2, Qt::AlignLeft);
// Fade time
mFadeBox = new KHBox(group);
mFadeBox->setMargin(0);
mFadeBox->setSpacing(KDialog::spacingHint());
grid->addWidget(mFadeBox, 3, 2, Qt::AlignLeft);
label = new QLabel(i18nc("@label:spinbox Time period over which to fade the sound", "Fade time:"), mFadeBox);
label->setFixedSize(label->sizeHint());
mFadeTime = new SpinBox(1, 999, mFadeBox);
mFadeTime->setSingleShiftStep(10);
mFadeTime->setFixedSize(mFadeTime->sizeHint());
label->setBuddy(mFadeTime);
connect(mFadeTime, SIGNAL(valueChanged(int)), SIGNAL(changed()));
label = new QLabel(i18nc("@label", "seconds"), mFadeBox);
label->setFixedSize(label->sizeHint());
mFadeBox->setWhatsThis(i18nc("@info:whatsthis", "Enter how many seconds to fade the sound before reaching the set volume."));
// Fade slider
mFadeVolumeBox = new KHBox(group);
mFadeVolumeBox->setMargin(0);
mFadeVolumeBox->setSpacing(KDialog::spacingHint());
grid->addWidget(mFadeVolumeBox, 4, 2);
label = new QLabel(i18nc("@label:slider", "Initial volume:"), mFadeVolumeBox);
label->setFixedSize(label->sizeHint());
mFadeSlider = new Slider(0, 100, 10, Qt::Horizontal, mFadeVolumeBox);
mFadeSlider->setTickPosition(QSlider::TicksBelow);
mFadeSlider->setTickInterval(10);
mFadeSlider->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
label->setBuddy(mFadeSlider);
connect(mFadeSlider, SIGNAL(valueChanged(int)), SIGNAL(changed()));
mFadeVolumeBox->setWhatsThis(i18nc("@info:whatsthis", "Choose the initial volume for playing the sound file."));
slotVolumeToggled(false);
}
SoundWidget::~SoundWidget()
{
delete mPlayer; // this stops playing if not already stopped
mPlayer = 0;
}
/******************************************************************************
* Set the controls' values.
*/
void SoundWidget::set(const QString& file, float volume, float fadeVolume, int fadeSeconds, int repeatPause)
{
// Initialise the control values
mFileEdit->setText(KAlarm::pathOrUrl(file));
if (mRepeatGroupBox)
{
mRepeatGroupBox->setChecked(repeatPause >= 0);
mRepeatPause->setValue(repeatPause >= 0 ? repeatPause : 0);
}
mVolumeCheckbox->setChecked(volume >= 0);
mVolumeSlider->setValue(volume >= 0 ? static_cast<int>(volume*100) : 100);
mFadeCheckbox->setChecked(fadeVolume >= 0);
mFadeSlider->setValue(fadeVolume >= 0 ? static_cast<int>(fadeVolume*100) : 100);
mFadeTime->setValue(fadeSeconds);
slotVolumeToggled(volume >= 0);
}
/******************************************************************************
* Set the read-only status of the widget.
*/
void SoundWidget::setReadOnly(bool readOnly)
{
if (readOnly != mReadOnly)
{
mFileEdit->setReadOnly(readOnly);
mFileBrowseButton->setReadOnly(readOnly);
if (mRepeatGroupBox)
mRepeatGroupBox->setReadOnly(readOnly);
mVolumeCheckbox->setReadOnly(readOnly);
mVolumeSlider->setReadOnly(readOnly);
mFadeCheckbox->setReadOnly(readOnly);
mFadeTime->setReadOnly(readOnly);
mFadeSlider->setReadOnly(readOnly);
mReadOnly = readOnly;
}
}
/******************************************************************************
* Return the file name typed in the edit field.
*/
QString SoundWidget::fileName() const
{
return mFileEdit->text();
}
/******************************************************************************
* Validate the entered file and return it.
*/
bool SoundWidget::file(KUrl& url, bool showErrorMessage) const
{
bool result = validate(showErrorMessage);
url = mUrl;
return result;
}
/******************************************************************************
* Return the entered repetition and volume settings:
* 'volume' is in range 0 - 1, or < 0 if volume is not to be set.
* 'fadeVolume is similar, with 'fadeTime' set to the fade interval in seconds.
*/
void SoundWidget::getVolume(float& volume, float& fadeVolume, int& fadeSeconds) const
{
volume = mVolumeCheckbox->isChecked() ? (float)mVolumeSlider->value() / 100 : -1;
if (mFadeCheckbox->isChecked())
{
fadeVolume = (float)mFadeSlider->value() / 100;
fadeSeconds = mFadeTime->value();
}
else
{
fadeVolume = -1;
fadeSeconds = 0;
}
}
/******************************************************************************
* Return the entered repetition setting.
* Reply = seconds to pause between repetitions, or -1 if no repeat.
*/
int SoundWidget::repeatPause() const
{
return mRepeatGroupBox && mRepeatGroupBox->isChecked() ? mRepeatPause->value() : -1;
}
/******************************************************************************
* Called when the dialog's size has changed.
* Records the new size in the config file.
*/
void SoundWidget::resizeEvent(QResizeEvent* re)
{
mVolumeSlider->resize(mFadeSlider->size());
QWidget::resizeEvent(re);
}
void SoundWidget::showEvent(QShowEvent* se)
{
mVolumeSlider->resize(mFadeSlider->size());
QWidget::showEvent(se);
}
/******************************************************************************
* Called when the file browser button is clicked.
*/
void SoundWidget::slotPickFile()
{
QString url = SoundPicker::browseFile(mDefaultDir, mFileEdit->text());
if (!url.isEmpty())
mFileEdit->setText(KAlarm::pathOrUrl(url));
}
/******************************************************************************
* Called when the file play or stop button is clicked.
*/
void SoundWidget::playSound()
{
if (mPlayer)
{
// The file is currently playing. Stop it.
playFinished();
return;
}
if (!validate(true))
return;
#if 0
#warning Phonon::createPlayer() does not work
mPlayer = Phonon::createPlayer(Phonon::MusicCategory, mUrl);
mPlayer->setParent(this);
#else
mPlayer = new Phonon::MediaObject(this);
Phonon::AudioOutput* output = new Phonon::AudioOutput(Phonon::MusicCategory, mPlayer);
mPlayer->setCurrentSource(mUrl);
Phonon::createPath(mPlayer, output);
#endif
connect(mPlayer, SIGNAL(finished()), SLOT(playFinished()));
mFilePlay->setIcon(SmallIcon(QLatin1String("media-playback-stop"))); // change the play button to a stop button
mFilePlay->setToolTip(i18nc("@info:tooltip", "Stop sound"));
mFilePlay->setWhatsThis(i18nc("@info:whatsthis", "Stop playing the sound"));
mPlayer->play();
}
/******************************************************************************
* Called when playing the file has completed, or to stop playing.
*/
void SoundWidget::playFinished()
{
delete mPlayer; // this stops playing if not already stopped
mPlayer = 0;
mFilePlay->setIcon(SmallIcon(QLatin1String("media-playback-start")));
mFilePlay->setToolTip(i18nc("@info:tooltip", "Test the sound"));
mFilePlay->setWhatsThis(i18nc("@info:whatsthis", "Play the selected sound file."));
}
/******************************************************************************
* Check whether the specified sound file exists.
*/
bool SoundWidget::validate(bool showErrorMessage) const
{
QString file = mFileEdit->text();
if (file == mValidatedFile && !file.isEmpty())
return true;
mValidatedFile = file;
if (file.isEmpty() && mEmptyFileAllowed)
{
mUrl.clear();
return true;
}
KAlarm::FileErr err = KAlarm::checkFileExists(file, mUrl);
if (err == KAlarm::FileErr_None)
return true;
if (err == KAlarm::FileErr_Nonexistent)
{
mUrl = KUrl(file);
if (mUrl.isLocalFile() && !file.startsWith(QLatin1String("/")))
{
// It's a relative path.
// Find the first sound resource that contains files.
QStringList soundDirs = KGlobal::dirs()->resourceDirs("sound");
if (!soundDirs.isEmpty())
{
QDir dir;
dir.setFilter(QDir::Files | QDir::Readable);
for (int i = 0, end = soundDirs.count(); i < end; ++i)
{
dir = soundDirs[i];
if (dir.isReadable() && dir.count() > 2)
{
mUrl.setPath(soundDirs[i]);
mUrl.addPath(file);
QString f = mUrl.toLocalFile();
err = KAlarm::checkFileExists(f, mUrl);
if (err == KAlarm::FileErr_None)
return true;
if (err != KAlarm::FileErr_Nonexistent)
{
file = f; // for inclusion in error message
break;
}
}
}
}
if (err == KAlarm::FileErr_Nonexistent)
{
mUrl.setPath(QDir::homePath());
mUrl.addPath(file);
QString f = mUrl.toLocalFile();
err = KAlarm::checkFileExists(f, mUrl);
if (err == KAlarm::FileErr_None)
return true;
if (err != KAlarm::FileErr_Nonexistent)
file = f; // for inclusion in error message
}
}
}
mFileEdit->setFocus();
if (showErrorMessage
&& KAlarm::showFileErrMessage(file, err, KAlarm::FileErr_BlankPlay, const_cast<SoundWidget*>(this)))
return true;
mValidatedFile.clear();
mUrl.clear();
return false;
}
/******************************************************************************
* Called when the Set Volume checkbox is toggled.
*/
void SoundWidget::slotVolumeToggled(bool on)
{
mVolumeSlider->setEnabled(on);
mFadeCheckbox->setEnabled(on);
slotFadeToggled(on && mFadeCheckbox->isChecked());
}
/******************************************************************************
* Called when the Fade checkbox is toggled.
*/
void SoundWidget::slotFadeToggled(bool on)
{
mFadeBox->setEnabled(on);
mFadeVolumeBox->setEnabled(on);
emit changed();
}
#include "moc_sounddlg.cpp"
// vim: et sw=4: