You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
k3b/src/k3baudioplayer.cpp

664 lines
16 KiB

/*
*
* $Id: k3baudioplayer.cpp 619556 2007-01-03 17:38:12Z trueg $
* Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
*
* This file is part of the K3b project.
* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
*
* 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.
* See the file "COPYING" for the exact licensing terms.
*/
#include "k3baudioplayer.h"
#include <k3bmsf.h>
#include "kcutlabel.h"
#include <tqlabel.h>
#include <tqtoolbutton.h>
#include <tqlayout.h>
#include <tqtimer.h>
#include <tqdatetime.h>
#include <tqfont.h>
#include <tqslider.h>
#include <tqlistview.h>
#include <tqfile.h>
#include <tqpalette.h>
#include <tqheader.h>
#include <tqevent.h>
#include <tqdragobject.h>
#include <tqptrlist.h>
#include <kurldrag.h>
#include <kiconloader.h>
#include <tdelocale.h>
#include <kurl.h>
#include <tdeaction.h>
#include <string.h>
#ifdef WITH_ARTS
#include <arts/artsflow.h>
#endif
#include <kdebug.h>
using namespace std;
K3bPlayListViewItem::K3bPlayListViewItem( const TQString& filename, TQListView* parent )
: TDEListViewItem( parent ), m_filename( filename )
{
m_length = 0;
m_bActive = false;
}
K3bPlayListViewItem::K3bPlayListViewItem( const TQString& filename, TQListView* parent, TQListViewItem* after )
: TDEListViewItem( parent, after ), m_filename( filename )
{
m_length = 0;
m_bActive = false;
}
K3bPlayListViewItem::~K3bPlayListViewItem()
{
}
TQString K3bPlayListViewItem::text( int c ) const
{
switch( c ) {
case 0:
{
int pos = m_filename.findRev("/");
if( pos >= 0 )
return m_filename.mid(pos+1);
return m_filename;
}
case 1:
if( m_length > 0 )
return K3b::Msf(m_length).toString(false);
default:
return "";
}
}
void K3bPlayListViewItem::paintCell( TQPainter* p, const TQColorGroup& cg, int c, int w, int a )
{
if( m_bActive ) {
// change the color of the text:
// change roles: Text, HighlightedText, HighLight
TQColorGroup newCg( cg );
// we assume the user has not configured a very dark color as base color
newCg.setColor( TQColorGroup::Text, red );
newCg.setColor( TQColorGroup::Highlight, red );
newCg.setColor( TQColorGroup::HighlightedText, white );
TDEListViewItem::paintCell( p, newCg, c, w, a );
}
else
TDEListViewItem::paintCell( p, cg, c, w, a );
}
K3bPlayListView::K3bPlayListView( TQWidget* parent, const char* name )
: TDEListView( parent, name )
{
addColumn( i18n("Filename") );
addColumn( i18n("Length") );
setAllColumnsShowFocus( true );
setAcceptDrops( true );
setDropVisualizer( true );
setDragEnabled(true);
setItemsMovable( true );
header()->setClickEnabled( false );
setSorting( -1 );
}
K3bPlayListView::~K3bPlayListView()
{
}
bool K3bPlayListView::acceptDrag( TQDropEvent* e ) const
{
// we accept textdrag (urls) and moved items (supported by TDEListView)
return KURLDrag::canDecode(e) || TDEListView::acceptDrag(e);
}
TQDragObject* K3bPlayListView::dragObject()
{
TQPtrList<TQListViewItem> list = selectedItems();
if( list.isEmpty() )
return 0;
TQPtrListIterator<TQListViewItem> it(list);
KURL::List urls;
for( ; it.current(); ++it )
urls.append( KURL( ((K3bPlayListViewItem*)it.current())->filename() ) );
return KURLDrag::newDrag( urls, viewport() );
}
K3bAudioPlayer::K3bAudioPlayer( TQWidget* parent, const char* name )
: TQWidget( parent, name )
#ifdef WITH_ARTS
, m_playObject( Arts::PlayObject::null() )
#endif
{
m_currentItem = 0L;
// initialize
// ------------------------------------------------------------------------
m_labelFilename = new KCutLabel( i18n("no file"), this );
m_labelOverallTime = new TQLabel( "00:00", this );
m_labelCurrentTime = new TQLabel( "00:00", this );
m_viewPlayList = new K3bPlayListView( this );
m_labelOverallTime->setAlignment( AlignHCenter | AlignVCenter );
m_labelCurrentTime->setAlignment( AlignHCenter | AlignVCenter );
m_labelOverallTime->setFrameStyle( TQFrame::StyledPanel | TQFrame::Plain );
m_labelCurrentTime->setFrameStyle( TQFrame::StyledPanel | TQFrame::Plain );
m_labelFilename->setFrameStyle( TQFrame::StyledPanel | TQFrame::Plain );
m_labelOverallTime->setPalette( TQPalette( TQColor(238, 238, 205) ) );
m_labelCurrentTime->setPalette( TQPalette( TQColor(238, 238, 205) ) );
m_labelFilename->setPalette( TQPalette( TQColor(238, 238, 205) ) );
m_buttonPlay = new TQToolButton( this );
m_buttonPause = new TQToolButton( this );
m_buttonStop = new TQToolButton( this );
m_buttonPlay->setIconSet( SmallIconSet("media-playback-start") );
m_buttonPause->setIconSet( SmallIconSet("media-playback-pause") );
m_buttonStop->setIconSet( SmallIconSet("media-playback-stop") );
m_buttonForward = new TQToolButton( this );
m_buttonBack = new TQToolButton( this );
m_buttonForward->setIconSet( SmallIconSet("media-skip-forward") );
m_buttonBack->setIconSet( SmallIconSet("media-skip-backward") );
m_seekSlider = new TQSlider( TQSlider::Horizontal, this );
m_updateTimer = new TQTimer( this );
// ------------------------------------------------------------------------
// layout
// ------------------------------------------------------------------------
TQGridLayout* grid = new TQGridLayout( this );
grid->setSpacing( 2 );
grid->setMargin( 0 );
grid->addWidget( m_buttonPlay, 1, 0 );
grid->addWidget( m_buttonPause, 1, 1 );
grid->addWidget( m_buttonStop, 1, 2 );
grid->addColSpacing( 3, 5 );
grid->addWidget( m_buttonBack, 1, 4 );
grid->addWidget( m_buttonForward, 1, 5 );
grid->addMultiCellWidget( m_labelFilename, 0, 0, 0, 6 );
grid->addMultiCellWidget( m_seekSlider, 1, 1, 6, 8 );
grid->addWidget( m_labelCurrentTime, 0, 7 );
grid->addWidget( m_labelOverallTime, 0, 8 );
grid->addMultiCellWidget( m_viewPlayList, 2, 2, 0, 8 );
grid->setRowStretch( 2, 1 );
grid->setColStretch( 6, 1 );
// ------------------------------------------------------------------------
// actions
// ------------------------------------------------------------------------
m_actionRemove = new TDEAction( i18n( "Remove" ), "edit-delete",
Key_Delete, this, TQ_SLOT(slotRemoveSelected()),
this, "audioplayer_remove" );
m_actionClear = new TDEAction( i18n( "Clear List" ), "edit-clear",
0, this, TQ_SLOT(clear()),
this, "audioplayer_clear" );
m_contextMenu = new TDEActionMenu( this, "audio_player_menu" );
m_contextMenu->insert(m_actionRemove);
m_contextMenu->insert(m_actionClear);
// ------------------------------------------------------------------------
// connections
// ------------------------------------------------------------------------
connect( m_viewPlayList, TQ_SIGNAL(contextMenu(TDEListView*, TQListViewItem*, const TQPoint&)),
this, TQ_SLOT(slotShowContextMenu(TDEListView*, TQListViewItem*, const TQPoint&)) );
connect( m_buttonPlay, TQ_SIGNAL(clicked()), this, TQ_SLOT(play()) );
connect( m_buttonStop, TQ_SIGNAL(clicked()), this, TQ_SLOT(stop()) );
connect( m_buttonPause, TQ_SIGNAL(clicked()), this, TQ_SLOT(pause()) );
connect( m_buttonForward, TQ_SIGNAL(clicked()), this, TQ_SLOT(forward()) );
connect( m_buttonBack, TQ_SIGNAL(clicked()), this, TQ_SLOT(back()) );
connect( m_seekSlider, TQ_SIGNAL(sliderMoved(int)), this, TQ_SLOT(seek(int)) );
connect( m_seekSlider, TQ_SIGNAL(valueChanged(int)), this, TQ_SLOT(slotUpdateCurrentTime(int)) );
connect( m_updateTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotUpdateDisplay()) );
connect( m_updateTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotCheckEnd()) );
connect( m_viewPlayList, TQ_SIGNAL(doubleClicked(TQListViewItem*)),
this, TQ_SLOT(slotPlayItem(TQListViewItem*)) );
connect( m_viewPlayList, TQ_SIGNAL(dropped(TQDropEvent*,TQListViewItem*)),
this, TQ_SLOT(slotDropped(TQDropEvent*,TQListViewItem*)) );
// ------------------------------------------------------------------------
m_bLengthReady = false;
}
K3bAudioPlayer::~K3bAudioPlayer()
{
// we remove the reference to the play object
// if we don't do this it won't be removed and K3b will crash (not sure why)
#ifdef WITH_ARTS
m_playObject = Arts::PlayObject::null();
#endif
}
int K3bAudioPlayer::state()
{
#ifdef WITH_ARTS
if( !m_playObject.isNull() ) {
switch( m_playObject.state() ) {
case Arts::posIdle:
return STOPPED;
case Arts::posPlaying:
return PLAYING;
case Arts::posPaused:
return PAUSED;
}
}
else if( m_currentItem )
return STOPPED;
#endif
return EMPTY;
}
void K3bAudioPlayer::playFile( const TQString& filename )
{
clear();
if( TQFile::exists( filename ) ) {
K3bPlayListViewItem* item = new K3bPlayListViewItem( filename, m_viewPlayList );
setCurrentItem( item );
play();
emit started( filename );
}
}
void K3bAudioPlayer::playFiles( const TQStringList& files )
{
clear();
TQStringList::ConstIterator it = files.begin();
playFile( *it );
++it;
for( ; it != files.end(); ++it )
enqueueFile( *it );
}
void K3bAudioPlayer::enqueueFile( const TQString& filename )
{
if( TQFile::exists( filename ) )
(void)new K3bPlayListViewItem( filename, m_viewPlayList, m_viewPlayList->lastChild() );
}
void K3bAudioPlayer::enqueueFiles( const TQStringList& files )
{
for( TQStringList::ConstIterator it = files.begin(); it != files.end(); ++it )
enqueueFile( *it );
}
void K3bAudioPlayer::play()
{
#ifdef WITH_ARTS
if( !m_currentItem ) {
setCurrentItem( m_viewPlayList->firstChild() );
}
if( m_currentItem ) {
if( m_playObject.isNull() ) {
Arts::PlayObjectFactory factory = Arts::Reference("global:Arts_PlayObjectFactory");
if( factory.isNull() ) {
kdDebug() << "(K3bAudioPlayer) could not create PlayObjectFactory. Possibly no artsd running." << endl;
m_labelFilename->setText( i18n("No running aRtsd found") );
return;
}
m_playObject = factory.createPlayObject( string(TQFile::encodeName(m_currentItem->filename()) ) );
if( m_playObject.isNull() ) {
kdDebug() << "(K3bAudioPlayer) no aRts module available for: " << m_currentItem->filename() << endl;
m_labelFilename->setText( i18n("Unknown file format") );
// play the next if there is any
if( m_currentItem->itemBelow() ) {
setCurrentItem( m_currentItem->itemBelow() );
play();
}
return;
}
}
if( m_playObject.state() != Arts::posPlaying ) {
m_playObject.play();
emit started();
m_updateTimer->start( 1000 );
}
slotUpdateFilename();
}
#endif
}
void K3bAudioPlayer::slotPlayItem( TQListViewItem* item )
{
setCurrentItem( item );
play();
}
void K3bAudioPlayer::stop()
{
#ifdef WITH_ARTS
if( !m_playObject.isNull() ) {
m_updateTimer->stop();
m_playObject.halt();
m_playObject = Arts::PlayObject::null();
m_bLengthReady = false;
emit stopped();
}
#endif
m_seekSlider->setValue(0);
slotUpdateFilename();
slotUpdateCurrentTime(0);
}
void K3bAudioPlayer::pause()
{
#ifdef WITH_ARTS
if( !m_playObject.isNull() ) {
if( m_playObject.state() == Arts::posPlaying ) {
m_updateTimer->stop();
m_playObject.pause();
emit paused();
}
slotUpdateFilename();
}
#endif
}
void K3bAudioPlayer::seek( long pos )
{
#ifdef WITH_ARTS
if( !m_playObject.isNull() ) {
if( m_playObject.state() != Arts::posIdle ) {
if( pos < 0 ) {
m_playObject.seek( Arts::poTime() );
}
else if( m_playObject.overallTime().seconds < pos ) {
m_playObject.seek( m_playObject.overallTime() );
}
else if( pos != m_playObject.currentTime().seconds ) {
m_playObject.seek( Arts::poTime( pos, 0, -1, "" ) );
}
}
}
else {
m_seekSlider->setValue(0);
slotUpdateCurrentTime(0);
}
#endif
}
void K3bAudioPlayer::seek( int pos )
{
seek( (long)pos );
}
void K3bAudioPlayer::forward()
{
if( m_currentItem ) {
if( m_currentItem->itemBelow() ) {
bool bPlay = false;
if( state() == PLAYING )
bPlay = true;
setCurrentItem( m_currentItem->itemBelow() );
if( bPlay )
play();
}
}
}
void K3bAudioPlayer::back()
{
if( m_currentItem ) {
if( m_currentItem->itemAbove() ) {
bool bPlay = false;
if( state() == PLAYING )
bPlay = true;
setCurrentItem( m_currentItem->itemAbove() );
if( bPlay )
play();
}
}
}
void K3bAudioPlayer::clear()
{
setCurrentItem( 0 );
m_viewPlayList->clear();
}
long K3bAudioPlayer::length()
{
#ifdef WITH_ARTS
if( !m_playObject.isNull() ) {
return m_playObject.overallTime().seconds;
}
#endif
return 0;
}
long K3bAudioPlayer::position()
{
#ifdef WITH_ARTS
if( !m_playObject.isNull() ) {
return m_playObject.currentTime().seconds;
}
#endif
return 0;
}
// FIXME: let my do some useful stuff!
bool K3bAudioPlayer::supportsMimetype( const TQString& mimetype )
{
if( mimetype.contains("audio") || mimetype.contains("ogg") )
return true;
else
return false;
}
void K3bAudioPlayer::slotCheckEnd()
{
#ifdef WITH_ARTS
if( !m_playObject.isNull() ) {
if( m_playObject.state() == Arts::posIdle ) {
if( m_currentItem->nextSibling() ) {
setCurrentItem( m_currentItem->nextSibling() );
play();
}
else {
stop();
}
emit ended();
}
}
#endif
}
void K3bAudioPlayer::setCurrentItem( TQListViewItem* item )
{
if( item == 0 ) {
stop();
m_labelOverallTime->setText("00:00");
m_labelFilename->setText( i18n("no file") );
m_currentItem = 0;
}
else if( K3bPlayListViewItem* playItem = dynamic_cast<K3bPlayListViewItem*>(item) ) {
if( m_currentItem ) {
// reset m_currentItem
m_currentItem->setActive( false );
stop();
}
m_currentItem = playItem;
m_currentItem->setActive( true );
// paint the activity changes
m_viewPlayList->viewport()->update();
slotUpdateFilename();
}
}
void K3bAudioPlayer::slotUpdateCurrentTime( int time )
{
m_labelCurrentTime->setText( K3b::Msf( time*75 ).toString(false) );
}
void K3bAudioPlayer::slotUpdateLength( long time )
{
m_labelOverallTime->setText( K3b::Msf( time*75 ).toString(false) );
}
void K3bAudioPlayer::slotUpdateFilename()
{
if( m_currentItem ) {
TQString display = m_currentItem->filename();
int pos = display.findRev("/");
if( pos >= 0 )
display = display.mid(pos+1);
switch( state() ) {
case PLAYING:
display.prepend( TQString("(%1) ").arg(i18n("playing")) );
break;
case PAUSED:
display.prepend( TQString("(%1) ").arg(i18n("paused")) );
break;
case STOPPED:
display.prepend( TQString("(%1) ").arg(i18n("stopped")) );
break;
default:
break;
}
m_labelFilename->setText( display );
}
}
void K3bAudioPlayer::slotUpdateDisplay()
{
if( m_currentItem ) {
// we need to set the length here because sometimes it is not ready in the beginning (?)
if( !m_bLengthReady && length() > 0 ) {
slotUpdateLength( length() );
m_seekSlider->setMaxValue( length() );
m_currentItem->setLength( 75 * length() );
m_bLengthReady = true;
m_viewPlayList->viewport()->update();
}
m_seekSlider->setValue( position() );
}
}
void K3bAudioPlayer::slotDropped( TQDropEvent* e, TQListViewItem* after )
{
if( !after )
after = m_viewPlayList->lastChild();
KURL::List urls;
KURLDrag::decode( e, urls );
for( KURL::List::ConstIterator it = urls.begin(); it != urls.end(); ++it ) {
if( TQFile::exists( (*it).path() ) ) {
TQListViewItem* newItem = new K3bPlayListViewItem( (*it).path(), m_viewPlayList, after );
after = newItem;
}
}
}
void K3bAudioPlayer::slotRemoveSelected()
{
TQPtrList<TQListViewItem> selected = m_viewPlayList->selectedItems();
for( TQListViewItem* item = selected.first(); item; item = selected.next() ) {
if( item == m_currentItem )
setCurrentItem(0);
delete item;
}
}
void K3bAudioPlayer::slotShowContextMenu( TDEListView*, TQListViewItem* item, const TQPoint& p )
{
if( item )
m_actionRemove->setEnabled( true );
else
m_actionRemove->setEnabled( false );
m_contextMenu->popup(p);
}
#include "k3baudioplayer.moc"