kalarm

sounddlg.cpp
1 /*
2  * sounddlg.cpp - sound file selection and configuration dialog
3  * Program: kalarm
4  * Copyright © 2005,2007,2008 by David Jarvie <djarvie@kde.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20 
21 #ifndef WITHOUT_ARTS
22 
23 #include "kalarm.h"
24 
25 #include <tqlabel.h>
26 #include <tqhbox.h>
27 #include <tqgroupbox.h>
28 #include <tqlayout.h>
29 #include <tqfile.h>
30 #include <tqdir.h>
31 #include <tqwhatsthis.h>
32 #include <tqtooltip.h>
33 
34 #include <tdelocale.h>
35 #include <kstandarddirs.h>
36 #include <kiconloader.h>
37 #ifdef WITHOUT_ARTS
38 #include <kaudioplayer.h>
39 #else
40 #include <tqtimer.h>
41 #include <arts/kartsdispatcher.h>
42 #include <arts/kartsserver.h>
43 #include <arts/kplayobjectfactory.h>
44 #include <arts/kplayobject.h>
45 #endif
46 #include <tdemessagebox.h>
47 #include <tdeio/netaccess.h>
48 #include <kdebug.h>
49 
50 #include "checkbox.h"
51 #include "functions.h"
52 #include "lineedit.h"
53 #include "mainwindow.h"
54 #include "pushbutton.h"
55 #include "slider.h"
56 #include "soundpicker.h"
57 #include "spinbox.h"
58 #include "sounddlg.moc"
59 
60 
61 // Collect these widget labels together to ensure consistent wording and
62 // translations across different modules.
63 TQString SoundDlg::i18n_SetVolume() { return i18n("Set volume"); }
64 TQString SoundDlg::i18n_v_SetVolume() { return i18n("Set &volume"); }
65 TQString SoundDlg::i18n_Repeat() { return i18n("Repeat"); }
66 TQString SoundDlg::i18n_p_Repeat() { return i18n("Re&peat"); }
67 
68 static const char SOUND_DIALOG_NAME[] = "SoundDialog";
69 
70 
71 SoundDlg::SoundDlg(const TQString& file, float volume, float fadeVolume, int fadeSeconds, bool repeat,
72  const TQString& caption, TQWidget* parent, const char* name)
73  : KDialogBase(parent, name, true, caption, Ok|Cancel, Ok, false),
74  mReadOnly(false),
75  mArtsDispatcher(0),
76  mPlayObject(0),
77  mPlayTimer(0)
78 {
79  TQWidget* page = new TQWidget(this);
80  setMainWidget(page);
81  TQVBoxLayout* layout = new TQVBoxLayout(page, 0, spacingHint());
82 
83  // File play button
84  TQHBox* box = new TQHBox(page);
85  layout->addWidget(box);
86  mFilePlay = new TQPushButton(box);
87  mFilePlay->setPixmap(SmallIcon("media-playback-start"));
88  mFilePlay->setFixedSize(mFilePlay->sizeHint());
89  connect(mFilePlay, TQ_SIGNAL(clicked()), TQ_SLOT(playSound()));
90  TQToolTip::add(mFilePlay, i18n("Test the sound"));
91  TQWhatsThis::add(mFilePlay, i18n("Play the selected sound file."));
92 
93  // File name edit box
94  mFileEdit = new LineEdit(LineEdit::Url, box);
95  mFileEdit->setAcceptDrops(true);
96  TQWhatsThis::add(mFileEdit, i18n("Enter the name or URL of a sound file to play."));
97 
98  // File browse button
99  mFileBrowseButton = new PushButton(box);
100  mFileBrowseButton->setPixmap(SmallIcon("document-open"));
101  mFileBrowseButton->setFixedSize(mFileBrowseButton->sizeHint());
102  connect(mFileBrowseButton, TQ_SIGNAL(clicked()), TQ_SLOT(slotPickFile()));
103  TQToolTip::add(mFileBrowseButton, i18n("Choose a file"));
104  TQWhatsThis::add(mFileBrowseButton, i18n("Select a sound file to play."));
105 
106  // Sound repetition checkbox
107  mRepeatCheckbox = new CheckBox(i18n_p_Repeat(), page);
108  mRepeatCheckbox->setFixedSize(mRepeatCheckbox->sizeHint());
109  TQWhatsThis::add(mRepeatCheckbox,
110  i18n("If checked, the sound file will be played repeatedly for as long as the message is displayed."));
111  layout->addWidget(mRepeatCheckbox);
112 
113  // Volume
114  TQGroupBox* group = new TQGroupBox(i18n("Volume"), page);
115  layout->addWidget(group);
116  TQGridLayout* grid = new TQGridLayout(group, 4, 3, marginHint(), spacingHint());
117  grid->addRowSpacing(0, fontMetrics().height() - marginHint() + spacingHint());
118  grid->setColStretch(2, 1);
119  int indentWidth = 3 * KDialog::spacingHint();
120  grid->addColSpacing(0, indentWidth);
121  grid->addColSpacing(1, indentWidth);
122  // Get alignment to use in TQGridLayout (AlignAuto doesn't work correctly there)
123  int alignment = TQApplication::reverseLayout() ? TQt::AlignRight : TQt::AlignLeft;
124 
125  // 'Set volume' checkbox
126  box = new TQHBox(group);
127  box->setSpacing(spacingHint());
128  grid->addMultiCellWidget(box, 1, 1, 0, 2);
129  mVolumeCheckbox = new CheckBox(i18n_v_SetVolume(), box);
130  mVolumeCheckbox->setFixedSize(mVolumeCheckbox->sizeHint());
131  connect(mVolumeCheckbox, TQ_SIGNAL(toggled(bool)), TQ_SLOT(slotVolumeToggled(bool)));
132  TQWhatsThis::add(mVolumeCheckbox,
133  i18n("Select to choose the volume for playing the sound file."));
134 
135  // Volume slider
136  mVolumeSlider = new Slider(0, 100, 10, 0, TQt::Horizontal, box);
137  mVolumeSlider->setTickmarks(TQSlider::Below);
138  mVolumeSlider->setTickInterval(10);
139  mVolumeSlider->setSizePolicy(TQSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Fixed));
140  TQWhatsThis::add(mVolumeSlider, i18n("Choose the volume for playing the sound file."));
141  mVolumeCheckbox->setFocusWidget(mVolumeSlider);
142 
143  // Fade checkbox
144  mFadeCheckbox = new CheckBox(i18n("Fade"), group);
145  mFadeCheckbox->setFixedSize(mFadeCheckbox->sizeHint());
146  connect(mFadeCheckbox, TQ_SIGNAL(toggled(bool)), TQ_SLOT(slotFadeToggled(bool)));
147  TQWhatsThis::add(mFadeCheckbox,
148  i18n("Select to fade the volume when the sound file first starts to play."));
149  grid->addMultiCellWidget(mFadeCheckbox, 2, 2, 1, 2, alignment);
150 
151  // Fade time
152  mFadeBox = new TQHBox(group);
153  mFadeBox->setSpacing(spacingHint());
154  grid->addWidget(mFadeBox, 3, 2, alignment);
155  TQLabel* label = new TQLabel(i18n("Time period over which to fade the sound", "Fade time:"), mFadeBox);
156  label->setFixedSize(label->sizeHint());
157  mFadeTime = new SpinBox(1, 999, 1, mFadeBox);
158  mFadeTime->setLineShiftStep(10);
159  mFadeTime->setFixedSize(mFadeTime->sizeHint());
160  label->setBuddy(mFadeTime);
161  label = new TQLabel(i18n("seconds"), mFadeBox);
162  label->setFixedSize(label->sizeHint());
163  TQWhatsThis::add(mFadeBox, i18n("Enter how many seconds to fade the sound before reaching the set volume."));
164 
165  // Fade slider
166  mFadeVolumeBox = new TQHBox(group);
167  mFadeVolumeBox->setSpacing(spacingHint());
168  grid->addWidget(mFadeVolumeBox, 4, 2);
169  label = new TQLabel(i18n("Initial volume:"), mFadeVolumeBox);
170  label->setFixedSize(label->sizeHint());
171  mFadeSlider = new Slider(0, 100, 10, 0, TQt::Horizontal, mFadeVolumeBox);
172  mFadeSlider->setTickmarks(TQSlider::Below);
173  mFadeSlider->setTickInterval(10);
174  mFadeSlider->setSizePolicy(TQSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Fixed));
175  label->setBuddy(mFadeSlider);
176  TQWhatsThis::add(mFadeVolumeBox, i18n("Choose the initial volume for playing the sound file."));
177 
178  // Restore the dialogue size from last time
179  TQSize s;
180  if (KAlarm::readConfigWindowSize(SOUND_DIALOG_NAME, s))
181  resize(s);
182 
183  // Initialise the control values
184  mFileEdit->setText(file);
185  mRepeatCheckbox->setChecked(repeat);
186  mVolumeCheckbox->setChecked(volume >= 0);
187  mVolumeSlider->setValue(volume >= 0 ? static_cast<int>(volume*100) : 100);
188  mFadeCheckbox->setChecked(fadeVolume >= 0);
189  mFadeSlider->setValue(fadeVolume >= 0 ? static_cast<int>(fadeVolume*100) : 100);
190  mFadeTime->setValue(fadeSeconds);
191  slotVolumeToggled(volume >= 0);
192 }
193 
194 SoundDlg::~SoundDlg()
195 {
196  stopPlay();
197 }
198 
199 /******************************************************************************
200 * Set the read-only status of the dialogue.
201 */
202 void SoundDlg::setReadOnly(bool readOnly)
203 {
204  if (readOnly != mReadOnly)
205  {
206  mFileEdit->setReadOnly(readOnly);
207  mFileBrowseButton->setReadOnly(readOnly);
208  mRepeatCheckbox->setReadOnly(readOnly);
209  mVolumeCheckbox->setReadOnly(readOnly);
210  mVolumeSlider->setReadOnly(readOnly);
211  mFadeCheckbox->setReadOnly(readOnly);
212  mFadeTime->setReadOnly(readOnly);
213  mFadeSlider->setReadOnly(readOnly);
214  mReadOnly = readOnly;
215  }
216 }
217 
218 /******************************************************************************
219 * Return the entered repetition and volume settings:
220 * 'volume' is in range 0 - 1, or < 0 if volume is not to be set.
221 * 'fadeVolume is similar, with 'fadeTime' set to the fade interval in seconds.
222 * Reply = whether to repeat or not.
223 */
224 bool SoundDlg::getSettings(float& volume, float& fadeVolume, int& fadeSeconds) const
225 {
226  volume = mVolumeCheckbox->isChecked() ? (float)mVolumeSlider->value() / 100 : -1;
227  if (mFadeCheckbox->isChecked())
228  {
229  fadeVolume = (float)mFadeSlider->value() / 100;
230  fadeSeconds = mFadeTime->value();
231  }
232  else
233  {
234  fadeVolume = -1;
235  fadeSeconds = 0;
236  }
237  return mRepeatCheckbox->isChecked();
238 }
239 
240 /******************************************************************************
241 * Called when the dialog's size has changed.
242 * Records the new size in the config file.
243 */
244 void SoundDlg::resizeEvent(TQResizeEvent* re)
245 {
246  if (isVisible())
247  KAlarm::writeConfigWindowSize(SOUND_DIALOG_NAME, re->size());
248  mVolumeSlider->resize(mFadeSlider->size());
249  KDialog::resizeEvent(re);
250 }
251 
252 void SoundDlg::showEvent(TQShowEvent* se)
253 {
254  mVolumeSlider->resize(mFadeSlider->size());
255  KDialog::showEvent(se);
256 }
257 
258 /******************************************************************************
259 * Called when the OK button is clicked.
260 */
261 void SoundDlg::slotOk()
262 {
263  if (mReadOnly)
264  reject();
265  if (!checkFile())
266  return;
267  accept();
268 }
269 
270 /******************************************************************************
271 * Called when the file browser button is clicked.
272 */
273 void SoundDlg::slotPickFile()
274 {
275  TQString url = SoundPicker::browseFile(mDefaultDir, mFileEdit->text());
276  if (!url.isEmpty())
277  mFileEdit->setText(url);
278 }
279 
280 /******************************************************************************
281 * Called when the file play/stop button is clicked.
282 */
283 void SoundDlg::playSound()
284 {
285 #ifdef WITHOUT_ARTS
286  if (checkFile())
287  KAudioPlayer::play(TQFile::encodeName(mFileName));
288 #else
289  if (mPlayObject)
290  {
291  stopPlay();
292  return;
293  }
294  if (!checkFile())
295  return;
296  KURL url(mFileName);
297  MainWindow* mmw = MainWindow::mainMainWindow();
298  if (!url.isValid() || !TDEIO::NetAccess::exists(url, true, mmw)
299  || !TDEIO::NetAccess::download(url, mLocalAudioFile, mmw))
300  {
301  kdError(5950) << "SoundDlg::playAudio(): Open failure: " << mFileName << endl;
302  KMessageBox::error(this, i18n("Cannot open audio file:\n%1").arg(mFileName));
303  return;
304  }
305  mPlayTimer = new TQTimer(this);
306  connect(mPlayTimer, TQ_SIGNAL(timeout()), TQ_SLOT(checkAudioPlay()));
307  mArtsDispatcher = new KArtsDispatcher;
308  mPlayStarted = false;
309  KArtsServer aserver;
310  Arts::SoundServerV2 sserver = aserver.server();
311  KDE::PlayObjectFactory factory(sserver);
312  mPlayObject = factory.createPlayObject(mLocalAudioFile, true);
313  mFilePlay->setPixmap(SmallIcon("media-playback-stop"));
314  TQToolTip::add(mFilePlay, i18n("Stop sound"));
315  TQWhatsThis::add(mFilePlay, i18n("Stop playing the sound"));
316  connect(mPlayObject, TQ_SIGNAL(playObjectCreated()), TQ_SLOT(checkAudioPlay()));
317  if (!mPlayObject->object().isNull())
318  checkAudioPlay();
319 #endif
320 }
321 
322 /******************************************************************************
323 * Called when the audio file has loaded and is ready to play, or on a timer
324 * when play is expected to have completed.
325 * If it is ready to play, start playing it (for the first time or repeated).
326 * If play has not yet completed, wait a bit longer.
327 */
328 void SoundDlg::checkAudioPlay()
329 {
330 #ifndef WITHOUT_ARTS
331  if (!mPlayObject)
332  return;
333  if (mPlayObject->state() == Arts::posIdle)
334  {
335  // The file has loaded and is ready to play, or play has completed
336  if (mPlayStarted)
337  {
338  // Play has completed
339  stopPlay();
340  return;
341  }
342 
343  // Start playing the file
344  kdDebug(5950) << "SoundDlg::checkAudioPlay(): start\n";
345  mPlayStarted = true;
346  mPlayObject->play();
347  }
348 
349  // The sound file is still playing
350  Arts::poTime overall = mPlayObject->overallTime();
351  Arts::poTime current = mPlayObject->currentTime();
352  int time = 1000*(overall.seconds - current.seconds) + overall.ms - current.ms;
353  if (time < 0)
354  time = 0;
355  kdDebug(5950) << "SoundDlg::checkAudioPlay(): wait for " << (time+100) << "ms\n";
356  mPlayTimer->start(time + 100, true);
357 #endif
358 }
359 
360 /******************************************************************************
361 * Called when play completes, the Silence button is clicked, or the window is
362 * closed, to terminate audio access.
363 */
364 void SoundDlg::stopPlay()
365 {
366 #ifndef WITHOUT_ARTS
367  delete mPlayObject; mPlayObject = 0;
368  delete mArtsDispatcher; mArtsDispatcher = 0;
369  delete mPlayTimer; mPlayTimer = 0;
370  if (!mLocalAudioFile.isEmpty())
371  {
372  TDEIO::NetAccess::removeTempFile(mLocalAudioFile); // removes it only if it IS a temporary file
373  mLocalAudioFile = TQString();
374  }
375  mFilePlay->setPixmap(SmallIcon("media-playback-start"));
376  TQToolTip::add(mFilePlay, i18n("Test the sound"));
377  TQWhatsThis::add(mFilePlay, i18n("Play the selected sound file."));
378 #endif
379 }
380 
381 /******************************************************************************
382 * Check whether the specified sound file exists.
383 * Note that KAudioPlayer::play() can only cope with local files.
384 */
385 bool SoundDlg::checkFile()
386 {
387  mFileName = mFileEdit->text();
388  KURL url;
389  if (KURL::isRelativeURL(mFileName))
390  {
391  // It's not an absolute URL, so check for an absolute path
392  TQFileInfo f(mFileName);
393  if (!f.isRelative())
394  url.setPath(mFileName);
395  }
396  else
397  url = KURL::fromPathOrURL(mFileName); // it's an absolute URL
398 #ifdef WITHOUT_ARTS
399  if (!url.isEmpty())
400  {
401  // It's an absolute path or URL.
402  // Only allow local files for KAudioPlayer.
403  if (url.isLocalFile() && TDEIO::NetAccess::exists(url, true, this))
404  {
405  mFileName = url.path();
406  return true;
407  }
408  }
409  else
410 #else
411  if (url.isEmpty())
412 #endif
413  {
414  // It's a relative path.
415  // Find the first sound resource that contains files.
416  TQStringList soundDirs = TDEGlobal::dirs()->resourceDirs("sound");
417  if (!soundDirs.isEmpty())
418  {
419  TQDir dir;
420  dir.setFilter(TQDir::Files | TQDir::Readable);
421  for (TQStringList::ConstIterator it = soundDirs.begin(); it != soundDirs.end(); ++it)
422  {
423  dir = *it;
424  if (dir.isReadable() && dir.count() > 2)
425  {
426  url.setPath(*it);
427  url.addPath(mFileName);
428  if (TDEIO::NetAccess::exists(url, true, this))
429  {
430  mFileName = url.path();
431  return true;
432  }
433  }
434  }
435  }
436  url.setPath(TQDir::homeDirPath());
437  url.addPath(mFileName);
438  if (TDEIO::NetAccess::exists(url, true, this))
439  {
440  mFileName = url.path();
441  return true;
442  }
443  }
444 #ifdef WITHOUT_ARTS
445  KMessageBox::sorry(this, i18n("File not found"));
446  mFileName = TQString();
447  return false;
448 #else
449  return true;
450 #endif
451 }
452 
453 /******************************************************************************
454 * Called when the Set Volume checkbox is toggled.
455 */
456 void SoundDlg::slotVolumeToggled(bool on)
457 {
458  mVolumeSlider->setEnabled(on);
459  mFadeCheckbox->setEnabled(on);
460  slotFadeToggled(on && mFadeCheckbox->isChecked());
461 }
462 
463 /******************************************************************************
464 * Called when the Fade checkbox is toggled.
465 */
466 void SoundDlg::slotFadeToggled(bool on)
467 {
468  mFadeBox->setEnabled(on);
469  mFadeVolumeBox->setEnabled(on);
470 }
471 
472 #endif // #ifndef WITHOUT_ARTS
miscellaneous functions
main application window