diff options
Diffstat (limited to 'kalarm/sounddlg.cpp')
-rw-r--r-- | kalarm/sounddlg.cpp | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/kalarm/sounddlg.cpp b/kalarm/sounddlg.cpp new file mode 100644 index 000000000..fe1df0bad --- /dev/null +++ b/kalarm/sounddlg.cpp @@ -0,0 +1,472 @@ +/* + * sounddlg.cpp - sound file selection and configuration dialog + * Program: kalarm + * Copyright © 2005,2007,2008 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. + */ + +#ifndef WITHOUT_ARTS + +#include "kalarm.h" + +#include <qlabel.h> +#include <qhbox.h> +#include <qgroupbox.h> +#include <qlayout.h> +#include <qfile.h> +#include <qdir.h> +#include <qwhatsthis.h> +#include <qtooltip.h> + +#include <klocale.h> +#include <kstandarddirs.h> +#include <kiconloader.h> +#ifdef WITHOUT_ARTS +#include <kaudioplayer.h> +#else +#include <qtimer.h> +#include <arts/kartsdispatcher.h> +#include <arts/kartsserver.h> +#include <arts/kplayobjectfactory.h> +#include <arts/kplayobject.h> +#endif +#include <kmessagebox.h> +#include <kio/netaccess.h> +#include <kdebug.h> + +#include "checkbox.h" +#include "functions.h" +#include "lineedit.h" +#include "mainwindow.h" +#include "pushbutton.h" +#include "slider.h" +#include "soundpicker.h" +#include "spinbox.h" +#include "sounddlg.moc" + + +// Collect these widget labels together to ensure consistent wording and +// translations across different modules. +QString SoundDlg::i18n_SetVolume() { return i18n("Set volume"); } +QString SoundDlg::i18n_v_SetVolume() { return i18n("Set &volume"); } +QString SoundDlg::i18n_Repeat() { return i18n("Repeat"); } +QString SoundDlg::i18n_p_Repeat() { return i18n("Re&peat"); } + +static const char SOUND_DIALOG_NAME[] = "SoundDialog"; + + +SoundDlg::SoundDlg(const QString& file, float volume, float fadeVolume, int fadeSeconds, bool repeat, + const QString& caption, QWidget* parent, const char* name) + : KDialogBase(parent, name, true, caption, Ok|Cancel, Ok, false), + mReadOnly(false), + mArtsDispatcher(0), + mPlayObject(0), + mPlayTimer(0) +{ + QWidget* page = new QWidget(this); + setMainWidget(page); + QVBoxLayout* layout = new QVBoxLayout(page, 0, spacingHint()); + + // File play button + QHBox* box = new QHBox(page); + layout->addWidget(box); + mFilePlay = new QPushButton(box); + mFilePlay->setPixmap(SmallIcon("player_play")); + mFilePlay->setFixedSize(mFilePlay->sizeHint()); + connect(mFilePlay, SIGNAL(clicked()), SLOT(playSound())); + QToolTip::add(mFilePlay, i18n("Test the sound")); + QWhatsThis::add(mFilePlay, i18n("Play the selected sound file.")); + + // File name edit box + mFileEdit = new LineEdit(LineEdit::Url, box); + mFileEdit->setAcceptDrops(true); + QWhatsThis::add(mFileEdit, i18n("Enter the name or URL of a sound file to play.")); + + // File browse button + mFileBrowseButton = new PushButton(box); + mFileBrowseButton->setPixmap(SmallIcon("fileopen")); + mFileBrowseButton->setFixedSize(mFileBrowseButton->sizeHint()); + connect(mFileBrowseButton, SIGNAL(clicked()), SLOT(slotPickFile())); + QToolTip::add(mFileBrowseButton, i18n("Choose a file")); + QWhatsThis::add(mFileBrowseButton, i18n("Select a sound file to play.")); + + // Sound repetition checkbox + mRepeatCheckbox = new CheckBox(i18n_p_Repeat(), page); + mRepeatCheckbox->setFixedSize(mRepeatCheckbox->sizeHint()); + QWhatsThis::add(mRepeatCheckbox, + i18n("If checked, the sound file will be played repeatedly for as long as the message is displayed.")); + layout->addWidget(mRepeatCheckbox); + + // Volume + QGroupBox* group = new QGroupBox(i18n("Volume"), page); + layout->addWidget(group); + QGridLayout* grid = new QGridLayout(group, 4, 3, marginHint(), spacingHint()); + grid->addRowSpacing(0, fontMetrics().height() - marginHint() + spacingHint()); + grid->setColStretch(2, 1); + int indentWidth = 3 * KDialog::spacingHint(); + grid->addColSpacing(0, indentWidth); + grid->addColSpacing(1, indentWidth); + // Get alignment to use in QGridLayout (AlignAuto doesn't work correctly there) + int alignment = QApplication::reverseLayout() ? Qt::AlignRight : Qt::AlignLeft; + + // 'Set volume' checkbox + box = new QHBox(group); + box->setSpacing(spacingHint()); + grid->addMultiCellWidget(box, 1, 1, 0, 2); + mVolumeCheckbox = new CheckBox(i18n_v_SetVolume(), box); + mVolumeCheckbox->setFixedSize(mVolumeCheckbox->sizeHint()); + connect(mVolumeCheckbox, SIGNAL(toggled(bool)), SLOT(slotVolumeToggled(bool))); + QWhatsThis::add(mVolumeCheckbox, + i18n("Select to choose the volume for playing the sound file.")); + + // Volume slider + mVolumeSlider = new Slider(0, 100, 10, 0, Qt::Horizontal, box); + mVolumeSlider->setTickmarks(QSlider::Below); + mVolumeSlider->setTickInterval(10); + mVolumeSlider->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); + QWhatsThis::add(mVolumeSlider, i18n("Choose the volume for playing the sound file.")); + mVolumeCheckbox->setFocusWidget(mVolumeSlider); + + // Fade checkbox + mFadeCheckbox = new CheckBox(i18n("Fade"), group); + mFadeCheckbox->setFixedSize(mFadeCheckbox->sizeHint()); + connect(mFadeCheckbox, SIGNAL(toggled(bool)), SLOT(slotFadeToggled(bool))); + QWhatsThis::add(mFadeCheckbox, + i18n("Select to fade the volume when the sound file first starts to play.")); + grid->addMultiCellWidget(mFadeCheckbox, 2, 2, 1, 2, alignment); + + // Fade time + mFadeBox = new QHBox(group); + mFadeBox->setSpacing(spacingHint()); + grid->addWidget(mFadeBox, 3, 2, alignment); + QLabel* label = new QLabel(i18n("Time period over which to fade the sound", "Fade time:"), mFadeBox); + label->setFixedSize(label->sizeHint()); + mFadeTime = new SpinBox(1, 999, 1, mFadeBox); + mFadeTime->setLineShiftStep(10); + mFadeTime->setFixedSize(mFadeTime->sizeHint()); + label->setBuddy(mFadeTime); + label = new QLabel(i18n("seconds"), mFadeBox); + label->setFixedSize(label->sizeHint()); + QWhatsThis::add(mFadeBox, i18n("Enter how many seconds to fade the sound before reaching the set volume.")); + + // Fade slider + mFadeVolumeBox = new QHBox(group); + mFadeVolumeBox->setSpacing(spacingHint()); + grid->addWidget(mFadeVolumeBox, 4, 2); + label = new QLabel(i18n("Initial volume:"), mFadeVolumeBox); + label->setFixedSize(label->sizeHint()); + mFadeSlider = new Slider(0, 100, 10, 0, Qt::Horizontal, mFadeVolumeBox); + mFadeSlider->setTickmarks(QSlider::Below); + mFadeSlider->setTickInterval(10); + mFadeSlider->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); + label->setBuddy(mFadeSlider); + QWhatsThis::add(mFadeVolumeBox, i18n("Choose the initial volume for playing the sound file.")); + + // Restore the dialogue size from last time + QSize s; + if (KAlarm::readConfigWindowSize(SOUND_DIALOG_NAME, s)) + resize(s); + + // Initialise the control values + mFileEdit->setText(file); + mRepeatCheckbox->setChecked(repeat); + 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); +} + +SoundDlg::~SoundDlg() +{ + stopPlay(); +} + +/****************************************************************************** +* Set the read-only status of the dialogue. +*/ +void SoundDlg::setReadOnly(bool readOnly) +{ + if (readOnly != mReadOnly) + { + mFileEdit->setReadOnly(readOnly); + mFileBrowseButton->setReadOnly(readOnly); + mRepeatCheckbox->setReadOnly(readOnly); + mVolumeCheckbox->setReadOnly(readOnly); + mVolumeSlider->setReadOnly(readOnly); + mFadeCheckbox->setReadOnly(readOnly); + mFadeTime->setReadOnly(readOnly); + mFadeSlider->setReadOnly(readOnly); + mReadOnly = readOnly; + } +} + +/****************************************************************************** +* 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. +* Reply = whether to repeat or not. +*/ +bool SoundDlg::getSettings(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 mRepeatCheckbox->isChecked(); +} + +/****************************************************************************** +* 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()); + mVolumeSlider->resize(mFadeSlider->size()); + KDialog::resizeEvent(re); +} + +void SoundDlg::showEvent(QShowEvent* se) +{ + mVolumeSlider->resize(mFadeSlider->size()); + KDialog::showEvent(se); +} + +/****************************************************************************** +* Called when the OK button is clicked. +*/ +void SoundDlg::slotOk() +{ + if (mReadOnly) + reject(); + if (!checkFile()) + return; + accept(); +} + +/****************************************************************************** +* Called when the file browser button is clicked. +*/ +void SoundDlg::slotPickFile() +{ + QString url = SoundPicker::browseFile(mDefaultDir, mFileEdit->text()); + if (!url.isEmpty()) + mFileEdit->setText(url); +} + +/****************************************************************************** +* Called when the file play/stop button is clicked. +*/ +void SoundDlg::playSound() +{ +#ifdef WITHOUT_ARTS + if (checkFile()) + KAudioPlayer::play(QFile::encodeName(mFileName)); +#else + if (mPlayObject) + { + stopPlay(); + return; + } + if (!checkFile()) + return; + KURL url(mFileName); + MainWindow* mmw = MainWindow::mainMainWindow(); + if (!url.isValid() || !KIO::NetAccess::exists(url, true, mmw) + || !KIO::NetAccess::download(url, mLocalAudioFile, mmw)) + { + kdError(5950) << "SoundDlg::playAudio(): Open failure: " << mFileName << endl; + KMessageBox::error(this, i18n("Cannot open audio file:\n%1").arg(mFileName)); + return; + } + mPlayTimer = new QTimer(this); + connect(mPlayTimer, SIGNAL(timeout()), SLOT(checkAudioPlay())); + mArtsDispatcher = new KArtsDispatcher; + mPlayStarted = false; + KArtsServer aserver; + Arts::SoundServerV2 sserver = aserver.server(); + KDE::PlayObjectFactory factory(sserver); + mPlayObject = factory.createPlayObject(mLocalAudioFile, true); + mFilePlay->setPixmap(SmallIcon("player_stop")); + QToolTip::add(mFilePlay, i18n("Stop sound")); + QWhatsThis::add(mFilePlay, i18n("Stop playing the sound")); + connect(mPlayObject, SIGNAL(playObjectCreated()), SLOT(checkAudioPlay())); + if (!mPlayObject->object().isNull()) + checkAudioPlay(); +#endif +} + +/****************************************************************************** +* Called when the audio file has loaded and is ready to play, or on a timer +* when play is expected to have completed. +* If it is ready to play, start playing it (for the first time or repeated). +* If play has not yet completed, wait a bit longer. +*/ +void SoundDlg::checkAudioPlay() +{ +#ifndef WITHOUT_ARTS + if (!mPlayObject) + return; + if (mPlayObject->state() == Arts::posIdle) + { + // The file has loaded and is ready to play, or play has completed + if (mPlayStarted) + { + // Play has completed + stopPlay(); + return; + } + + // Start playing the file + kdDebug(5950) << "SoundDlg::checkAudioPlay(): start\n"; + mPlayStarted = true; + mPlayObject->play(); + } + + // The sound file is still playing + Arts::poTime overall = mPlayObject->overallTime(); + Arts::poTime current = mPlayObject->currentTime(); + int time = 1000*(overall.seconds - current.seconds) + overall.ms - current.ms; + if (time < 0) + time = 0; + kdDebug(5950) << "SoundDlg::checkAudioPlay(): wait for " << (time+100) << "ms\n"; + mPlayTimer->start(time + 100, true); +#endif +} + +/****************************************************************************** +* Called when play completes, the Silence button is clicked, or the window is +* closed, to terminate audio access. +*/ +void SoundDlg::stopPlay() +{ +#ifndef WITHOUT_ARTS + delete mPlayObject; mPlayObject = 0; + delete mArtsDispatcher; mArtsDispatcher = 0; + delete mPlayTimer; mPlayTimer = 0; + if (!mLocalAudioFile.isEmpty()) + { + KIO::NetAccess::removeTempFile(mLocalAudioFile); // removes it only if it IS a temporary file + mLocalAudioFile = QString::null; + } + mFilePlay->setPixmap(SmallIcon("player_play")); + QToolTip::add(mFilePlay, i18n("Test the sound")); + QWhatsThis::add(mFilePlay, i18n("Play the selected sound file.")); +#endif +} + +/****************************************************************************** +* Check whether the specified sound file exists. +* Note that KAudioPlayer::play() can only cope with local files. +*/ +bool SoundDlg::checkFile() +{ + mFileName = mFileEdit->text(); + KURL url; + if (KURL::isRelativeURL(mFileName)) + { + // It's not an absolute URL, so check for an absolute path + QFileInfo f(mFileName); + if (!f.isRelative()) + url.setPath(mFileName); + } + else + url = KURL::fromPathOrURL(mFileName); // it's an absolute URL +#ifdef WITHOUT_ARTS + if (!url.isEmpty()) + { + // It's an absolute path or URL. + // Only allow local files for KAudioPlayer. + if (url.isLocalFile() && KIO::NetAccess::exists(url, true, this)) + { + mFileName = url.path(); + return true; + } + } + else +#else + if (url.isEmpty()) +#endif + { + // 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 (QStringList::ConstIterator it = soundDirs.begin(); it != soundDirs.end(); ++it) + { + dir = *it; + if (dir.isReadable() && dir.count() > 2) + { + url.setPath(*it); + url.addPath(mFileName); + if (KIO::NetAccess::exists(url, true, this)) + { + mFileName = url.path(); + return true; + } + } + } + } + url.setPath(QDir::homeDirPath()); + url.addPath(mFileName); + if (KIO::NetAccess::exists(url, true, this)) + { + mFileName = url.path(); + return true; + } + } +#ifdef WITHOUT_ARTS + KMessageBox::sorry(this, i18n("File not found")); + mFileName = QString::null; + return false; +#else + return true; +#endif +} + +/****************************************************************************** +* Called when the Set Volume checkbox is toggled. +*/ +void SoundDlg::slotVolumeToggled(bool on) +{ + mVolumeSlider->setEnabled(on); + mFadeCheckbox->setEnabled(on); + slotFadeToggled(on && mFadeCheckbox->isChecked()); +} + +/****************************************************************************** +* Called when the Fade checkbox is toggled. +*/ +void SoundDlg::slotFadeToggled(bool on) +{ + mFadeBox->setEnabled(on); + mFadeVolumeBox->setEnabled(on); +} + +#endif // #ifndef WITHOUT_ARTS |