sounddlg.cpp
00001 /* 00002 * sounddlg.cpp - sound file selection and configuration dialog 00003 * Program: kalarm 00004 * Copyright © 2005,2007,2008 by David Jarvie <djarvie@kde.org> 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License along 00017 * with this program; if not, write to the Free Software Foundation, Inc., 00018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #ifndef WITHOUT_ARTS 00022 00023 #include "kalarm.h" 00024 00025 #include <tqlabel.h> 00026 #include <tqhbox.h> 00027 #include <tqgroupbox.h> 00028 #include <tqlayout.h> 00029 #include <tqfile.h> 00030 #include <tqdir.h> 00031 #include <tqwhatsthis.h> 00032 #include <tqtooltip.h> 00033 00034 #include <klocale.h> 00035 #include <kstandarddirs.h> 00036 #include <kiconloader.h> 00037 #ifdef WITHOUT_ARTS 00038 #include <kaudioplayer.h> 00039 #else 00040 #include <tqtimer.h> 00041 #include <arts/kartsdispatcher.h> 00042 #include <arts/kartsserver.h> 00043 #include <arts/kplayobjectfactory.h> 00044 #include <arts/kplayobject.h> 00045 #endif 00046 #include <kmessagebox.h> 00047 #include <kio/netaccess.h> 00048 #include <kdebug.h> 00049 00050 #include "checkbox.h" 00051 #include "functions.h" 00052 #include "lineedit.h" 00053 #include "mainwindow.h" 00054 #include "pushbutton.h" 00055 #include "slider.h" 00056 #include "soundpicker.h" 00057 #include "spinbox.h" 00058 #include "sounddlg.moc" 00059 00060 00061 // Collect these widget labels together to ensure consistent wording and 00062 // translations across different modules. 00063 TQString SoundDlg::i18n_SetVolume() { return i18n("Set volume"); } 00064 TQString SoundDlg::i18n_v_SetVolume() { return i18n("Set &volume"); } 00065 TQString SoundDlg::i18n_Repeat() { return i18n("Repeat"); } 00066 TQString SoundDlg::i18n_p_Repeat() { return i18n("Re&peat"); } 00067 00068 static const char SOUND_DIALOG_NAME[] = "SoundDialog"; 00069 00070 00071 SoundDlg::SoundDlg(const TQString& file, float volume, float fadeVolume, int fadeSeconds, bool repeat, 00072 const TQString& caption, TQWidget* parent, const char* name) 00073 : KDialogBase(parent, name, true, caption, Ok|Cancel, Ok, false), 00074 mReadOnly(false), 00075 mArtsDispatcher(0), 00076 mPlayObject(0), 00077 mPlayTimer(0) 00078 { 00079 TQWidget* page = new TQWidget(this); 00080 setMainWidget(page); 00081 TQVBoxLayout* layout = new TQVBoxLayout(page, 0, spacingHint()); 00082 00083 // File play button 00084 TQHBox* box = new TQHBox(page); 00085 layout->addWidget(box); 00086 mFilePlay = new TQPushButton(box); 00087 mFilePlay->setPixmap(SmallIcon("player_play")); 00088 mFilePlay->setFixedSize(mFilePlay->sizeHint()); 00089 connect(mFilePlay, TQT_SIGNAL(clicked()), TQT_SLOT(playSound())); 00090 TQToolTip::add(mFilePlay, i18n("Test the sound")); 00091 TQWhatsThis::add(mFilePlay, i18n("Play the selected sound file.")); 00092 00093 // File name edit box 00094 mFileEdit = new LineEdit(LineEdit::Url, box); 00095 mFileEdit->setAcceptDrops(true); 00096 TQWhatsThis::add(mFileEdit, i18n("Enter the name or URL of a sound file to play.")); 00097 00098 // File browse button 00099 mFileBrowseButton = new PushButton(box); 00100 mFileBrowseButton->setPixmap(SmallIcon("fileopen")); 00101 mFileBrowseButton->setFixedSize(mFileBrowseButton->sizeHint()); 00102 connect(mFileBrowseButton, TQT_SIGNAL(clicked()), TQT_SLOT(slotPickFile())); 00103 TQToolTip::add(mFileBrowseButton, i18n("Choose a file")); 00104 TQWhatsThis::add(mFileBrowseButton, i18n("Select a sound file to play.")); 00105 00106 // Sound repetition checkbox 00107 mRepeatCheckbox = new CheckBox(i18n_p_Repeat(), page); 00108 mRepeatCheckbox->setFixedSize(mRepeatCheckbox->sizeHint()); 00109 TQWhatsThis::add(mRepeatCheckbox, 00110 i18n("If checked, the sound file will be played repeatedly for as long as the message is displayed.")); 00111 layout->addWidget(mRepeatCheckbox); 00112 00113 // Volume 00114 TQGroupBox* group = new TQGroupBox(i18n("Volume"), page); 00115 layout->addWidget(group); 00116 TQGridLayout* grid = new TQGridLayout(group, 4, 3, marginHint(), spacingHint()); 00117 grid->addRowSpacing(0, fontMetrics().height() - marginHint() + spacingHint()); 00118 grid->setColStretch(2, 1); 00119 int indentWidth = 3 * KDialog::spacingHint(); 00120 grid->addColSpacing(0, indentWidth); 00121 grid->addColSpacing(1, indentWidth); 00122 // Get alignment to use in TQGridLayout (AlignAuto doesn't work correctly there) 00123 int alignment = TQApplication::reverseLayout() ? TQt::AlignRight : TQt::AlignLeft; 00124 00125 // 'Set volume' checkbox 00126 box = new TQHBox(group); 00127 box->setSpacing(spacingHint()); 00128 grid->addMultiCellWidget(box, 1, 1, 0, 2); 00129 mVolumeCheckbox = new CheckBox(i18n_v_SetVolume(), box); 00130 mVolumeCheckbox->setFixedSize(mVolumeCheckbox->sizeHint()); 00131 connect(mVolumeCheckbox, TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotVolumeToggled(bool))); 00132 TQWhatsThis::add(mVolumeCheckbox, 00133 i18n("Select to choose the volume for playing the sound file.")); 00134 00135 // Volume slider 00136 mVolumeSlider = new Slider(0, 100, 10, 0, Qt::Horizontal, box); 00137 mVolumeSlider->setTickmarks(TQSlider::Below); 00138 mVolumeSlider->setTickInterval(10); 00139 mVolumeSlider->setSizePolicy(TQSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Fixed)); 00140 TQWhatsThis::add(mVolumeSlider, i18n("Choose the volume for playing the sound file.")); 00141 mVolumeCheckbox->setFocusWidget(mVolumeSlider); 00142 00143 // Fade checkbox 00144 mFadeCheckbox = new CheckBox(i18n("Fade"), group); 00145 mFadeCheckbox->setFixedSize(mFadeCheckbox->sizeHint()); 00146 connect(mFadeCheckbox, TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotFadeToggled(bool))); 00147 TQWhatsThis::add(mFadeCheckbox, 00148 i18n("Select to fade the volume when the sound file first starts to play.")); 00149 grid->addMultiCellWidget(mFadeCheckbox, 2, 2, 1, 2, alignment); 00150 00151 // Fade time 00152 mFadeBox = new TQHBox(group); 00153 mFadeBox->setSpacing(spacingHint()); 00154 grid->addWidget(mFadeBox, 3, 2, alignment); 00155 TQLabel* label = new TQLabel(i18n("Time period over which to fade the sound", "Fade time:"), mFadeBox); 00156 label->setFixedSize(label->sizeHint()); 00157 mFadeTime = new SpinBox(1, 999, 1, mFadeBox); 00158 mFadeTime->setLineShiftStep(10); 00159 mFadeTime->setFixedSize(mFadeTime->sizeHint()); 00160 label->setBuddy(mFadeTime); 00161 label = new TQLabel(i18n("seconds"), mFadeBox); 00162 label->setFixedSize(label->sizeHint()); 00163 TQWhatsThis::add(mFadeBox, i18n("Enter how many seconds to fade the sound before reaching the set volume.")); 00164 00165 // Fade slider 00166 mFadeVolumeBox = new TQHBox(group); 00167 mFadeVolumeBox->setSpacing(spacingHint()); 00168 grid->addWidget(mFadeVolumeBox, 4, 2); 00169 label = new TQLabel(i18n("Initial volume:"), mFadeVolumeBox); 00170 label->setFixedSize(label->sizeHint()); 00171 mFadeSlider = new Slider(0, 100, 10, 0, Qt::Horizontal, mFadeVolumeBox); 00172 mFadeSlider->setTickmarks(TQSlider::Below); 00173 mFadeSlider->setTickInterval(10); 00174 mFadeSlider->setSizePolicy(TQSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Fixed)); 00175 label->setBuddy(mFadeSlider); 00176 TQWhatsThis::add(mFadeVolumeBox, i18n("Choose the initial volume for playing the sound file.")); 00177 00178 // Restore the dialogue size from last time 00179 TQSize s; 00180 if (KAlarm::readConfigWindowSize(SOUND_DIALOG_NAME, s)) 00181 resize(s); 00182 00183 // Initialise the control values 00184 mFileEdit->setText(file); 00185 mRepeatCheckbox->setChecked(repeat); 00186 mVolumeCheckbox->setChecked(volume >= 0); 00187 mVolumeSlider->setValue(volume >= 0 ? static_cast<int>(volume*100) : 100); 00188 mFadeCheckbox->setChecked(fadeVolume >= 0); 00189 mFadeSlider->setValue(fadeVolume >= 0 ? static_cast<int>(fadeVolume*100) : 100); 00190 mFadeTime->setValue(fadeSeconds); 00191 slotVolumeToggled(volume >= 0); 00192 } 00193 00194 SoundDlg::~SoundDlg() 00195 { 00196 stopPlay(); 00197 } 00198 00199 /****************************************************************************** 00200 * Set the read-only status of the dialogue. 00201 */ 00202 void SoundDlg::setReadOnly(bool readOnly) 00203 { 00204 if (readOnly != mReadOnly) 00205 { 00206 mFileEdit->setReadOnly(readOnly); 00207 mFileBrowseButton->setReadOnly(readOnly); 00208 mRepeatCheckbox->setReadOnly(readOnly); 00209 mVolumeCheckbox->setReadOnly(readOnly); 00210 mVolumeSlider->setReadOnly(readOnly); 00211 mFadeCheckbox->setReadOnly(readOnly); 00212 mFadeTime->setReadOnly(readOnly); 00213 mFadeSlider->setReadOnly(readOnly); 00214 mReadOnly = readOnly; 00215 } 00216 } 00217 00218 /****************************************************************************** 00219 * Return the entered repetition and volume settings: 00220 * 'volume' is in range 0 - 1, or < 0 if volume is not to be set. 00221 * 'fadeVolume is similar, with 'fadeTime' set to the fade interval in seconds. 00222 * Reply = whether to repeat or not. 00223 */ 00224 bool SoundDlg::getSettings(float& volume, float& fadeVolume, int& fadeSeconds) const 00225 { 00226 volume = mVolumeCheckbox->isChecked() ? (float)mVolumeSlider->value() / 100 : -1; 00227 if (mFadeCheckbox->isChecked()) 00228 { 00229 fadeVolume = (float)mFadeSlider->value() / 100; 00230 fadeSeconds = mFadeTime->value(); 00231 } 00232 else 00233 { 00234 fadeVolume = -1; 00235 fadeSeconds = 0; 00236 } 00237 return mRepeatCheckbox->isChecked(); 00238 } 00239 00240 /****************************************************************************** 00241 * Called when the dialog's size has changed. 00242 * Records the new size in the config file. 00243 */ 00244 void SoundDlg::resizeEvent(TQResizeEvent* re) 00245 { 00246 if (isVisible()) 00247 KAlarm::writeConfigWindowSize(SOUND_DIALOG_NAME, re->size()); 00248 mVolumeSlider->resize(mFadeSlider->size()); 00249 KDialog::resizeEvent(re); 00250 } 00251 00252 void SoundDlg::showEvent(TQShowEvent* se) 00253 { 00254 mVolumeSlider->resize(mFadeSlider->size()); 00255 KDialog::showEvent(se); 00256 } 00257 00258 /****************************************************************************** 00259 * Called when the OK button is clicked. 00260 */ 00261 void SoundDlg::slotOk() 00262 { 00263 if (mReadOnly) 00264 reject(); 00265 if (!checkFile()) 00266 return; 00267 accept(); 00268 } 00269 00270 /****************************************************************************** 00271 * Called when the file browser button is clicked. 00272 */ 00273 void SoundDlg::slotPickFile() 00274 { 00275 TQString url = SoundPicker::browseFile(mDefaultDir, mFileEdit->text()); 00276 if (!url.isEmpty()) 00277 mFileEdit->setText(url); 00278 } 00279 00280 /****************************************************************************** 00281 * Called when the file play/stop button is clicked. 00282 */ 00283 void SoundDlg::playSound() 00284 { 00285 #ifdef WITHOUT_ARTS 00286 if (checkFile()) 00287 KAudioPlayer::play(TQFile::encodeName(mFileName)); 00288 #else 00289 if (mPlayObject) 00290 { 00291 stopPlay(); 00292 return; 00293 } 00294 if (!checkFile()) 00295 return; 00296 KURL url(mFileName); 00297 MainWindow* mmw = MainWindow::mainMainWindow(); 00298 if (!url.isValid() || !KIO::NetAccess::exists(url, true, mmw) 00299 || !KIO::NetAccess::download(url, mLocalAudioFile, mmw)) 00300 { 00301 kdError(5950) << "SoundDlg::playAudio(): Open failure: " << mFileName << endl; 00302 KMessageBox::error(this, i18n("Cannot open audio file:\n%1").arg(mFileName)); 00303 return; 00304 } 00305 mPlayTimer = new TQTimer(this); 00306 connect(mPlayTimer, TQT_SIGNAL(timeout()), TQT_SLOT(checkAudioPlay())); 00307 mArtsDispatcher = new KArtsDispatcher; 00308 mPlayStarted = false; 00309 KArtsServer aserver; 00310 Arts::SoundServerV2 sserver = aserver.server(); 00311 KDE::PlayObjectFactory factory(sserver); 00312 mPlayObject = factory.createPlayObject(mLocalAudioFile, true); 00313 mFilePlay->setPixmap(SmallIcon("player_stop")); 00314 TQToolTip::add(mFilePlay, i18n("Stop sound")); 00315 TQWhatsThis::add(mFilePlay, i18n("Stop playing the sound")); 00316 connect(mPlayObject, TQT_SIGNAL(playObjectCreated()), TQT_SLOT(checkAudioPlay())); 00317 if (!mPlayObject->object().isNull()) 00318 checkAudioPlay(); 00319 #endif 00320 } 00321 00322 /****************************************************************************** 00323 * Called when the audio file has loaded and is ready to play, or on a timer 00324 * when play is expected to have completed. 00325 * If it is ready to play, start playing it (for the first time or repeated). 00326 * If play has not yet completed, wait a bit longer. 00327 */ 00328 void SoundDlg::checkAudioPlay() 00329 { 00330 #ifndef WITHOUT_ARTS 00331 if (!mPlayObject) 00332 return; 00333 if (mPlayObject->state() == Arts::posIdle) 00334 { 00335 // The file has loaded and is ready to play, or play has completed 00336 if (mPlayStarted) 00337 { 00338 // Play has completed 00339 stopPlay(); 00340 return; 00341 } 00342 00343 // Start playing the file 00344 kdDebug(5950) << "SoundDlg::checkAudioPlay(): start\n"; 00345 mPlayStarted = true; 00346 mPlayObject->play(); 00347 } 00348 00349 // The sound file is still playing 00350 Arts::poTime overall = mPlayObject->overallTime(); 00351 Arts::poTime current = mPlayObject->currentTime(); 00352 int time = 1000*(overall.seconds - current.seconds) + overall.ms - current.ms; 00353 if (time < 0) 00354 time = 0; 00355 kdDebug(5950) << "SoundDlg::checkAudioPlay(): wait for " << (time+100) << "ms\n"; 00356 mPlayTimer->start(time + 100, true); 00357 #endif 00358 } 00359 00360 /****************************************************************************** 00361 * Called when play completes, the Silence button is clicked, or the window is 00362 * closed, to terminate audio access. 00363 */ 00364 void SoundDlg::stopPlay() 00365 { 00366 #ifndef WITHOUT_ARTS 00367 delete mPlayObject; mPlayObject = 0; 00368 delete mArtsDispatcher; mArtsDispatcher = 0; 00369 delete mPlayTimer; mPlayTimer = 0; 00370 if (!mLocalAudioFile.isEmpty()) 00371 { 00372 KIO::NetAccess::removeTempFile(mLocalAudioFile); // removes it only if it IS a temporary file 00373 mLocalAudioFile = TQString(); 00374 } 00375 mFilePlay->setPixmap(SmallIcon("player_play")); 00376 TQToolTip::add(mFilePlay, i18n("Test the sound")); 00377 TQWhatsThis::add(mFilePlay, i18n("Play the selected sound file.")); 00378 #endif 00379 } 00380 00381 /****************************************************************************** 00382 * Check whether the specified sound file exists. 00383 * Note that KAudioPlayer::play() can only cope with local files. 00384 */ 00385 bool SoundDlg::checkFile() 00386 { 00387 mFileName = mFileEdit->text(); 00388 KURL url; 00389 if (KURL::isRelativeURL(mFileName)) 00390 { 00391 // It's not an absolute URL, so check for an absolute path 00392 TQFileInfo f(mFileName); 00393 if (!f.isRelative()) 00394 url.setPath(mFileName); 00395 } 00396 else 00397 url = KURL::fromPathOrURL(mFileName); // it's an absolute URL 00398 #ifdef WITHOUT_ARTS 00399 if (!url.isEmpty()) 00400 { 00401 // It's an absolute path or URL. 00402 // Only allow local files for KAudioPlayer. 00403 if (url.isLocalFile() && KIO::NetAccess::exists(url, true, this)) 00404 { 00405 mFileName = url.path(); 00406 return true; 00407 } 00408 } 00409 else 00410 #else 00411 if (url.isEmpty()) 00412 #endif 00413 { 00414 // It's a relative path. 00415 // Find the first sound resource that contains files. 00416 TQStringList soundDirs = KGlobal::dirs()->resourceDirs("sound"); 00417 if (!soundDirs.isEmpty()) 00418 { 00419 TQDir dir; 00420 dir.setFilter(TQDir::Files | TQDir::Readable); 00421 for (TQStringList::ConstIterator it = soundDirs.begin(); it != soundDirs.end(); ++it) 00422 { 00423 dir = *it; 00424 if (dir.isReadable() && dir.count() > 2) 00425 { 00426 url.setPath(*it); 00427 url.addPath(mFileName); 00428 if (KIO::NetAccess::exists(url, true, this)) 00429 { 00430 mFileName = url.path(); 00431 return true; 00432 } 00433 } 00434 } 00435 } 00436 url.setPath(TQDir::homeDirPath()); 00437 url.addPath(mFileName); 00438 if (KIO::NetAccess::exists(url, true, this)) 00439 { 00440 mFileName = url.path(); 00441 return true; 00442 } 00443 } 00444 #ifdef WITHOUT_ARTS 00445 KMessageBox::sorry(this, i18n("File not found")); 00446 mFileName = TQString(); 00447 return false; 00448 #else 00449 return true; 00450 #endif 00451 } 00452 00453 /****************************************************************************** 00454 * Called when the Set Volume checkbox is toggled. 00455 */ 00456 void SoundDlg::slotVolumeToggled(bool on) 00457 { 00458 mVolumeSlider->setEnabled(on); 00459 mFadeCheckbox->setEnabled(on); 00460 slotFadeToggled(on && mFadeCheckbox->isChecked()); 00461 } 00462 00463 /****************************************************************************** 00464 * Called when the Fade checkbox is toggled. 00465 */ 00466 void SoundDlg::slotFadeToggled(bool on) 00467 { 00468 mFadeBox->setEnabled(on); 00469 mFadeVolumeBox->setEnabled(on); 00470 } 00471 00472 #endif // #ifndef WITHOUT_ARTS