You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
473 lines
15 KiB
473 lines
15 KiB
15 years ago
|
/*
|
||
|
* 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
|