/* * sounddlg.cpp - sound file selection and configuration dialog * Program: kalarm * Copyright © 2005,2007,2008 by David Jarvie * * 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 #include #include #include #include #include #include #include #include #include #include #ifdef WITHOUT_ARTS #include #else #include #include #include #include #include #endif #include #include #include #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. TQString SoundDlg::i18n_SetVolume() { return i18n("Set volume"); } TQString SoundDlg::i18n_v_SetVolume() { return i18n("Set &volume"); } TQString SoundDlg::i18n_Repeat() { return i18n("Repeat"); } TQString SoundDlg::i18n_p_Repeat() { return i18n("Re&peat"); } static const char SOUND_DIALOG_NAME[] = "SoundDialog"; SoundDlg::SoundDlg(const TQString& file, float volume, float fadeVolume, int fadeSeconds, bool repeat, const TQString& caption, TQWidget* parent, const char* name) : KDialogBase(parent, name, true, caption, Ok|Cancel, Ok, false), mReadOnly(false), mArtsDispatcher(0), mPlayObject(0), mPlayTimer(0) { TQWidget* page = new TQWidget(this); setMainWidget(page); TQVBoxLayout* layout = new TQVBoxLayout(page, 0, spacingHint()); // File play button TQHBox* box = new TQHBox(page); layout->addWidget(box); mFilePlay = new TQPushButton(box); mFilePlay->setPixmap(SmallIcon("media-playback-start")); mFilePlay->setFixedSize(mFilePlay->sizeHint()); connect(mFilePlay, TQT_SIGNAL(clicked()), TQT_SLOT(playSound())); TQToolTip::add(mFilePlay, i18n("Test the sound")); TQWhatsThis::add(mFilePlay, i18n("Play the selected sound file.")); // File name edit box mFileEdit = new LineEdit(LineEdit::Url, box); mFileEdit->setAcceptDrops(true); TQWhatsThis::add(mFileEdit, i18n("Enter the name or URL of a sound file to play.")); // File browse button mFileBrowseButton = new PushButton(box); mFileBrowseButton->setPixmap(SmallIcon("document-open")); mFileBrowseButton->setFixedSize(mFileBrowseButton->sizeHint()); connect(mFileBrowseButton, TQT_SIGNAL(clicked()), TQT_SLOT(slotPickFile())); TQToolTip::add(mFileBrowseButton, i18n("Choose a file")); TQWhatsThis::add(mFileBrowseButton, i18n("Select a sound file to play.")); // Sound repetition checkbox mRepeatCheckbox = new CheckBox(i18n_p_Repeat(), page); mRepeatCheckbox->setFixedSize(mRepeatCheckbox->sizeHint()); TQWhatsThis::add(mRepeatCheckbox, i18n("If checked, the sound file will be played repeatedly for as long as the message is displayed.")); layout->addWidget(mRepeatCheckbox); // Volume TQGroupBox* group = new TQGroupBox(i18n("Volume"), page); layout->addWidget(group); TQGridLayout* grid = new TQGridLayout(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 TQGridLayout (AlignAuto doesn't work correctly there) int alignment = TQApplication::reverseLayout() ? TQt::AlignRight : TQt::AlignLeft; // 'Set volume' checkbox box = new TQHBox(group); box->setSpacing(spacingHint()); grid->addMultiCellWidget(box, 1, 1, 0, 2); mVolumeCheckbox = new CheckBox(i18n_v_SetVolume(), box); mVolumeCheckbox->setFixedSize(mVolumeCheckbox->sizeHint()); connect(mVolumeCheckbox, TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotVolumeToggled(bool))); TQWhatsThis::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(TQSlider::Below); mVolumeSlider->setTickInterval(10); mVolumeSlider->setSizePolicy(TQSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Fixed)); TQWhatsThis::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, TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotFadeToggled(bool))); TQWhatsThis::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 TQHBox(group); mFadeBox->setSpacing(spacingHint()); grid->addWidget(mFadeBox, 3, 2, alignment); TQLabel* label = new TQLabel(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 TQLabel(i18n("seconds"), mFadeBox); label->setFixedSize(label->sizeHint()); TQWhatsThis::add(mFadeBox, i18n("Enter how many seconds to fade the sound before reaching the set volume.")); // Fade slider mFadeVolumeBox = new TQHBox(group); mFadeVolumeBox->setSpacing(spacingHint()); grid->addWidget(mFadeVolumeBox, 4, 2); label = new TQLabel(i18n("Initial volume:"), mFadeVolumeBox); label->setFixedSize(label->sizeHint()); mFadeSlider = new Slider(0, 100, 10, 0, Qt::Horizontal, mFadeVolumeBox); mFadeSlider->setTickmarks(TQSlider::Below); mFadeSlider->setTickInterval(10); mFadeSlider->setSizePolicy(TQSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Fixed)); label->setBuddy(mFadeSlider); TQWhatsThis::add(mFadeVolumeBox, i18n("Choose the initial volume for playing the sound file.")); // Restore the dialogue size from last time TQSize 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(volume*100) : 100); mFadeCheckbox->setChecked(fadeVolume >= 0); mFadeSlider->setValue(fadeVolume >= 0 ? static_cast(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(TQResizeEvent* re) { if (isVisible()) KAlarm::writeConfigWindowSize(SOUND_DIALOG_NAME, re->size()); mVolumeSlider->resize(mFadeSlider->size()); KDialog::resizeEvent(re); } void SoundDlg::showEvent(TQShowEvent* 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() { TQString 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(TQFile::encodeName(mFileName)); #else if (mPlayObject) { stopPlay(); return; } if (!checkFile()) return; KURL url(mFileName); MainWindow* mmw = MainWindow::mainMainWindow(); if (!url.isValid() || !TDEIO::NetAccess::exists(url, true, mmw) || !TDEIO::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 TQTimer(this); connect(mPlayTimer, TQT_SIGNAL(timeout()), TQT_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("media-playback-stop")); TQToolTip::add(mFilePlay, i18n("Stop sound")); TQWhatsThis::add(mFilePlay, i18n("Stop playing the sound")); connect(mPlayObject, TQT_SIGNAL(playObjectCreated()), TQT_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()) { TDEIO::NetAccess::removeTempFile(mLocalAudioFile); // removes it only if it IS a temporary file mLocalAudioFile = TQString(); } mFilePlay->setPixmap(SmallIcon("media-playback-start")); TQToolTip::add(mFilePlay, i18n("Test the sound")); TQWhatsThis::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 TQFileInfo 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() && TDEIO::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. TQStringList soundDirs = TDEGlobal::dirs()->resourceDirs("sound"); if (!soundDirs.isEmpty()) { TQDir dir; dir.setFilter(TQDir::Files | TQDir::Readable); for (TQStringList::ConstIterator it = soundDirs.begin(); it != soundDirs.end(); ++it) { dir = *it; if (dir.isReadable() && dir.count() > 2) { url.setPath(*it); url.addPath(mFileName); if (TDEIO::NetAccess::exists(url, true, this)) { mFileName = url.path(); return true; } } } } url.setPath(TQDir::homeDirPath()); url.addPath(mFileName); if (TDEIO::NetAccess::exists(url, true, this)) { mFileName = url.path(); return true; } } #ifdef WITHOUT_ARTS KMessageBox::sorry(this, i18n("File not found")); mFileName = TQString(); 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