kalarm

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 <tdelocale.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 <tdemessagebox.h>
00047 #include <tdeio/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("media-playback-start"));
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("document-open"));
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()  ||  !TDEIO::NetAccess::exists(url, true, mmw)
00299     ||  !TDEIO::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("media-playback-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         TDEIO::NetAccess::removeTempFile(mLocalAudioFile);   // removes it only if it IS a temporary file
00373         mLocalAudioFile = TQString();
00374     }
00375     mFilePlay->setPixmap(SmallIcon("media-playback-start"));
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()  &&  TDEIO::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 = TDEGlobal::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 (TDEIO::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 (TDEIO::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