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 <tdestandarddirs.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.
63TQString SoundDlg::i18n_SetVolume() { return i18n("Set volume"); }
64TQString SoundDlg::i18n_v_SetVolume() { return i18n("Set &volume"); }
65TQString SoundDlg::i18n_Repeat() { return i18n("Repeat"); }
66TQString SoundDlg::i18n_p_Repeat() { return i18n("Re&peat"); }
67
68static const char SOUND_DIALOG_NAME[] = "SoundDialog";
69
70
71SoundDlg::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
194SoundDlg::~SoundDlg()
195{
196 stopPlay();
197}
198
199/******************************************************************************
200* Set the read-only status of the dialogue.
201*/
202void 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*/
224bool 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*/
244void 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
252void 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*/
261void 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*/
273void 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*/
283void 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*/
328void 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*/
364void 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*/
385bool 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*/
456void 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*/
466void 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