diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 19:09:31 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 19:09:31 +0000 |
commit | f2cfda2a54780868dfe0af7bd652fcd4906547da (patch) | |
tree | c6ac23545528f5701818424f2af5f79ce3665e6c /src/replaygainfilelist.cpp | |
download | soundkonverter-f2cfda2a54780868dfe0af7bd652fcd4906547da.tar.gz soundkonverter-f2cfda2a54780868dfe0af7bd652fcd4906547da.zip |
Added KDE3 version of SoundKonverter
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/soundkonverter@1097614 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/replaygainfilelist.cpp')
-rwxr-xr-x | src/replaygainfilelist.cpp | 1262 |
1 files changed, 1262 insertions, 0 deletions
diff --git a/src/replaygainfilelist.cpp b/src/replaygainfilelist.cpp new file mode 100755 index 0000000..2d71806 --- /dev/null +++ b/src/replaygainfilelist.cpp @@ -0,0 +1,1262 @@ + +#include "replaygainfilelist.h" +#include "tagengine.h" +#include "logger.h" +#include "config.h" +#include "replaygain.h" +#include "replaygainpluginloader.h" + +#include <qdir.h> +#include <qpainter.h> +#include <qsimplerichtext.h> +#include <qapplication.h> +#include <qheader.h> +#include <qlayout.h> +#include <qtimer.h> + +#include <klocale.h> +#include <kiconloader.h> +#include <kurldrag.h> +#include <kpopupmenu.h> +#include <kaction.h> +#include <kactioncollection.h> +#include <kmessagebox.h> +#include <kmountpoint.h> +#include <kprogress.h> +#include <kprocess.h> +#include <kmimetype.h> +#include <kapplication.h> +#include <kuser.h> + +// TODO move listDir, addFiles, addDir, etc -- done? + +// ### soundkonverter 0.4: give the 'track' and 'album' column a minimum width and fill the rest of the space with the 'file' column - make some margins, too + + +ReplayGainFileListItem::ReplayGainFileListItem( QListView* parent ) + : KListViewItem( parent ) +{ + m_type = File; + mimeType = "application/octet-stream"; + addingReplayGain = false; + queued = false; +} + +// ReplayGainFileListItem::ReplayGainFileListItem( QListView* parent, QListViewItem* after ) +// : KListViewItem( parent, after ) +// { +// m_type = File; +// mimeType = "application/octet-stream"; +// addingReplayGain = false; +// queued = false; +// } + +ReplayGainFileListItem::ReplayGainFileListItem( ReplayGainFileListItem* parent ) + : KListViewItem( parent ) +{ + m_type = File; + mimeType = "application/octet-stream"; + addingReplayGain = false; + queued = false; +} + +ReplayGainFileListItem::~ReplayGainFileListItem() +{} + +void ReplayGainFileListItem::paintCell( QPainter *p, const QColorGroup &cg, int column, int width, int alignment ) +{ + // NOTE speed up this function + // NOTE calculate the red color + + QColorGroup _cg( cg ); + QColor c; + + if( column == ((ReplayGainFileList*)listView())->columnByName(i18n("File")) ) + { + int margin = listView()->itemMargin(); + int w = width - 2*margin; + int h = height(); + QRect textRect = p->boundingRect( margin, 0, w, h, alignment, text(column) ); + + if( textRect.width() > w ) { + alignment = Qt::AlignRight | Qt::SingleLine; + } + } + + if( isSelected() && addingReplayGain ) { + _cg.setColor( QColorGroup::Highlight, QColor( 215, 62, 62 ) ); + QListViewItem::paintCell( p, _cg, column, width, alignment ); + return; + } + else if( addingReplayGain && column != listView()->sortColumn() ) { + _cg.setColor( QColorGroup::Base, QColor( 255, 234, 234 ) ); + QListViewItem::paintCell( p, _cg, column, width, alignment ); + return; + } + else if( addingReplayGain && column == listView()->sortColumn() ) { + _cg.setColor( QColorGroup::Base, QColor( 247, 227, 227 ) ); + QListViewItem::paintCell( p, _cg, column, width, alignment ); + return; + } + + if( isSelected() && queued ) { + _cg.setColor( QColorGroup::Highlight, QColor( 230, 232, 100 ) ); + QListViewItem::paintCell( p, _cg, column, width, alignment ); + return; + } + else if( queued && column != listView()->sortColumn() ) { + _cg.setColor( QColorGroup::Base, QColor( 255, 255, 190 ) ); + QListViewItem::paintCell( p, _cg, column, width, alignment ); + return; + } + else if( queued && column == listView()->sortColumn() ) { + _cg.setColor( QColorGroup::Base, QColor( 255, 243, 168 ) ); + QListViewItem::paintCell( p, _cg, column, width, alignment ); + return; + } + + KListViewItem::paintCell( p, _cg, column, width, alignment ); +} + +void ReplayGainFileListItem::setType( Type type ) +{ + if( type == m_type ) return; + + m_type = type; + + if( type == Album ) { + setOpen( true ); + setPixmap( 0, KGlobal::iconLoader()->loadIcon("cdrom_unmount",KIcon::Small) ); + } +} + +void ReplayGainFileListItem::updateReplayGainCells( TagData* tags ) +{ + if( !tags ) { + setText( ((ReplayGainFileList*)listView())->columnByName(i18n("Track")), i18n("Unknown") ); + setText( ((ReplayGainFileList*)listView())->columnByName(i18n("Album")), i18n("Unknown") ); + } + else { + if( tags->track_gain != 210588 ) { + setText( ((ReplayGainFileList*)listView())->columnByName(i18n("Track")), QString().sprintf("%+.2f dB",tags->track_gain) ); + } + else { + setText( ((ReplayGainFileList*)listView())->columnByName(i18n("Track")), i18n("Unknown") ); + } + if( tags->album_gain != 210588 ) { + setText( ((ReplayGainFileList*)listView())->columnByName(i18n("Album")), QString().sprintf("%+.2f dB",tags->album_gain) ); + } + else { + setText( ((ReplayGainFileList*)listView())->columnByName(i18n("Album")), i18n("Unknown") ); + } + } +} + +int ReplayGainFileListItem::compare( QListViewItem* item, int column, bool ascending ) const +{ + // NOTE looking at the types, not the strings would be better + if( text(1) == "" && item->text(1) != "" ) return -1; + else if( text(1) != "" && item->text(1) == "" ) return 1; + else return KListViewItem::compare( item, column, ascending ); +} + +ReplayGainFileList::ReplayGainFileList( TagEngine* _tagEngine, Config* _config, Logger* _logger, QWidget *parent, const char *name ) + : KListView( parent, name ) +{ + tagEngine = _tagEngine; + config = _config; + logger = _logger; + + processing = false; + queue = false; + + time = 0; + processedTime = 0; + + addColumn( i18n("File"), 390 ); + setColumnWidthMode( 0, QListView::Manual ); + addColumn( i18n("Track"), 90 ); + setColumnAlignment( 1, Qt::AlignRight ); + addColumn( i18n("Album"), 90 ); + setColumnAlignment( 2, Qt::AlignRight ); + + setSelectionMode( QListView::Extended ); + setAllColumnsShowFocus( true ); + setResizeMode( QListView::LastColumn ); + setShowSortIndicator( true ); + setSorting( 0 ); + setRootIsDecorated( true ); + + setDragEnabled( true ); + setAcceptDrops( true ); + + QGridLayout* grid = new QGridLayout( this, 2, 1, 11, 6 ); + grid->setRowStretch( 0, 1 ); + grid->setRowStretch( 2, 1 ); + grid->setColStretch( 0, 1 ); + grid->setColStretch( 2, 1 ); + pScanStatus = new KProgress( this, "pScanStatus" ); + pScanStatus->setMinimumHeight( pScanStatus->height() ); + pScanStatus->setFormat( "%v / %m" ); + pScanStatus->hide(); + grid->addWidget( pScanStatus, 1, 1 ); + grid->setColStretch( 1, 2 ); + + contextMenu = new KPopupMenu( this ); + connect( this, SIGNAL(contextMenuRequested(QListViewItem*,const QPoint&,int)), + this, SLOT(showContextMenu(QListViewItem*,const QPoint&,int)) + ); + + // we haven't got access to the action collection of soundKonverter, so let's create a new one + actionCollection = new KActionCollection( this ); + + calc_gain = new KAction( i18n("Calculate Replay Gain tags"), "apply", 0, this, SLOT(calcSelectedItemsGain()), actionCollection, "calc_album" ); + remove_gain = new KAction( i18n("Remove Replay Gain tags"), "cancel", 0, this, SLOT(removeSelectedItemsGain()), actionCollection, "remove_gain" ); + remove = new KAction( i18n("Remove"), "edittrash", Key_Delete, this, SLOT(removeSelectedItems()), actionCollection, "remove" ); + paste = new KAction( i18n("Paste"), "editpaste", 0, this, 0, actionCollection, "paste" ); + newalbum = new KAction( i18n("New album"), "filenew", 0, this, SLOT(createNewAlbum()), actionCollection, "newalbum" ); + open_albums = new KAction( i18n("Open all albums"), "view_tree", 0, this, SLOT(openAlbums()), actionCollection, "open_albums" ); + close_albums = new KAction( i18n("Cloase all albums"), "view_text", 0, this, SLOT(closeAlbums()), actionCollection, "close_albums" ); + + replayGain = new ReplayGain( config, logger ); + + bubble = new QSimpleRichText( i18n( "<div align=center>" + "<h3>Replay Gain Tool</h3>" + "With this tool you can add Replay Gain tags to your audio files and remove them." + //"<br>Replay Gain adds a volume correction information to the files for playing them at the same volume." + //"Replay Gain allows you to play all audio files at the same volume level without modifying the audio data." + "<br>Replay Gain adds a volume correction information to the files so that they can be played at an equal volume level." +// "<br><a href=\"documenation:replaygaintool\">Learn more about Replay Gain ...</a><br/>" + "</div>" ), QApplication::font() ); + + connect( header(), SIGNAL(sizeChange( int, int, int )), + SLOT(columnResizeEvent( int, int, int )) + ); + connect( this, SIGNAL( dropped(QDropEvent*, QListViewItem*, QListViewItem*) ), + SLOT( slotDropped(QDropEvent*, QListViewItem*, QListViewItem*) ) + ); + + process = new KProcess(); + connect( process, SIGNAL(receivedStdout(KProcess*,char*,int)), + this, SLOT(processOutput(KProcess*,char*,int)) + ); + connect( process, SIGNAL(receivedStderr(KProcess*,char*,int)), + this, SLOT(processOutput(KProcess*,char*,int)) + ); + connect( process, SIGNAL(processExited(KProcess*)), + this, SLOT(processExit(KProcess*)) + ); + + tUpdateProgress = new QTimer( this, "tUpdateProgress" ); + connect( tUpdateProgress, SIGNAL(timeout()), + this, SLOT(update()) + ); +} + +ReplayGainFileList::~ReplayGainFileList() +{ + delete replayGain; +} + +int ReplayGainFileList::columnByName( const QString& name ) +{ + for( int i = 0; i < columns(); ++i ) { + if( columnText( i ) == name ) return i; + } + return -1; +} + +void ReplayGainFileList::viewportPaintEvent( QPaintEvent* e ) +{ + KListView::viewportPaintEvent( e ); + + // the bubble help + if( childCount() == 0 ) { + QPainter p( viewport() ); + + bubble->setWidth( width() - 50 ); + + const uint w = bubble->width() + 20; + const uint h = bubble->height() + 20; + + p.setBrush( colorGroup().background() ); + p.drawRoundRect( 15, 15, w, h, (8*200)/w, (8*200)/h ); + bubble->draw( &p, 20, 20, QRect(), colorGroup() ); + } +} + +void ReplayGainFileList::viewportResizeEvent( QResizeEvent* ) +{ + // needed for correct redraw of bubble help + triggerUpdate(); +} + +void ReplayGainFileList::columnResizeEvent( int, int, int ) +{ + // needed for correct redraw of bubble help + triggerUpdate(); +} + +bool ReplayGainFileList::acceptDrag( QDropEvent* e ) const +{ + return ( e->source() == viewport() || KURLDrag::canDecode(e) ); // TODO verify the files +} + +void ReplayGainFileList::slotDropped( QDropEvent* e, QListViewItem*, QListViewItem* ) +{ + QString file; + KURL::List list; + if( KURLDrag::decode( e, list ) ) + { + for( KURL::List::Iterator it = list.begin(); it != list.end(); ++it ) + { + // TODO verify the files (necessary when multiple files are being dropped) + file = QDir::convertSeparators( (*it).pathOrURL() ); + QFileInfo fileInfo( file ); + if( fileInfo.isDir() ) + { + addDir( file ); + } + else + { + addFile( (*it).url() ); + } + } + } +} + +void ReplayGainFileList::contentsDragEnterEvent( QDragEnterEvent *e ) +{ + e->accept( e->source() == viewport() || KURLDrag::canDecode(e) ); +} + +void ReplayGainFileList::contentsDragMoveEvent( QDragMoveEvent *e ) +{ // the mouse has moved while dragging some stuff + + // if the file is added from an external app, don't let the user select the position to drop + if( e->source() != viewport() ) { + setDropHighlighter( false ); + setDropVisualizer( false ); + cleanItemHighlighter(); + cleanDropVisualizer(); + + // the rest is handled by KListView + KListView::contentsDragMoveEvent( e ); + + return; + } + + // translate the coordinates of the mouse pointer + QPoint vp = contentsToViewport( e->pos() ); + // and get the list view item below the pointer + ReplayGainFileListItem* item = itemAt( vp ); + + if( item && item->type() == ReplayGainFileListItem::Album ) { // the pointer is above an 'album' element + // draw a nice rect around the item + setDropHighlighter( true ); + setDropVisualizer( false ); + cleanDropVisualizer(); + } + else if( item ) { // the pointer is above an 'file' element + // draw a line above or below the item + setDropVisualizer( true ); + setDropHighlighter( false ); + cleanItemHighlighter(); + } + + // the rest is handled by KListView + KListView::contentsDragMoveEvent( e ); +} + +void ReplayGainFileList::contentsDropEvent( QDropEvent *e ) +{ // the stuff has been dropped + + emit dropped( e, 0, 0 ); // NOTE it works this way + + bool formatError = false; + + cleanDropVisualizer(); + cleanItemHighlighter(); + + // get the item below the mouse pointer, where the stuff has been dropped + QPoint vp = contentsToViewport( e->pos() ); + ReplayGainFileListItem* newParent = itemAt( vp ); + + // if the item is a 'file', use the parent item + if( newParent && newParent->type() != ReplayGainFileListItem::Album ) { + newParent = newParent->parent(); + } + + // TODO if the mouse is on the left side under the parent but not under the sibling item, use the parent +/* if( newParent == 0 && vp.x() >= 2*treeStepSize() ) { + QPoint p = vp; + int height = 0; + if( firstChild() ) { + height = firstChild()->height(); + } + p.setY( p.y() - height ); + ReplayGainFileListItem* it = itemAt( p ); + if( it && it->type() != ReplayGainFileListItem::Album ) { + newParent = it->parent(); + } + else if( it ) { + newParent = it; + } + }*/ + + ReplayGainFileListItem* i; + + // iterate through all items and move all selected ones + for( ReplayGainFileListItem* item = firstChild(); item != 0; ) { + if( item->type() == ReplayGainFileListItem::File ) { + if( newParent == 0 ) { + item = item->nextSibling(); + continue; + } + if( item->isSelected() ) { + if( newParent == 0 || newParent->mimeType == item->mimeType || newParent->mimeType == "application/octet-stream" ) { + if( newParent->mimeType == "application/octet-stream" ) newParent->mimeType = item->mimeType; + i = item; + item = item->nextSibling(); + takeItem( i ); + if( newParent ) newParent->insertItem( i ); + else insertItem( i ); + } + else { + item = item->nextSibling(); + formatError = true; + } + } + else { + item = item->nextSibling(); + } + } + else { + if( item == newParent ) { + item = item->nextSibling(); + continue; + } + if( item->isSelected() ) { + if( newParent == 0 || newParent->mimeType == item->mimeType || newParent->mimeType == "application/octet-stream" ) { + if( newParent->mimeType == "application/octet-stream" ) newParent->mimeType = item->mimeType; + for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; ) { + i = sub_item; + sub_item = sub_item->nextSibling(); + item->takeItem( i ); + if( newParent ) newParent->insertItem( i ); + else insertItem( i ); + } + i = item; + item = item->nextSibling(); + delete i; + } + else { + item = item->nextSibling(); + formatError = true; + } + } + else { + for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; ) { + if( sub_item->isSelected() ) { + if( newParent == 0 || newParent->mimeType == item->mimeType || newParent->mimeType == "application/octet-stream" ) { + if( newParent && newParent->mimeType == "application/octet-stream" ) newParent->mimeType = item->mimeType; + i = sub_item; + sub_item = sub_item->nextSibling(); + item->takeItem( i ); + if( newParent ) newParent->insertItem( i ); + else insertItem( i ); + } + else { + sub_item = sub_item->nextSibling(); + formatError = true; + } + } + else { + sub_item = sub_item->nextSibling(); + } + } + if( item->childCount() == 0 ) { + i = item; + item = item->nextSibling(); + delete i; + } + else { + item = item->nextSibling(); + } + } + } + } + + // FIXME make the mouse pointer look normal + + if( formatError ) { + KMessageBox::information( this, + i18n("You can't place files of different formats in the same \'album\'."), i18n("Different file formats") ); + } +} + +int ReplayGainFileList::listDir( const QString& directory, QStringList filter, bool recursive, bool fast, int count ) +{ // NOTE speed up? + QDir dir( directory ); + dir.setFilter( QDir::Files | QDir::Dirs | QDir::NoSymLinks | QDir::Readable ); + + QStringList list = dir.entryList(); + + for( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) { + if( *it == "." || *it == ".." ) continue; + QFileInfo fileInfo( directory + "/" + *it ); + if( fast ) { + if( fileInfo.isDir() && recursive ) { + count = listDir( directory + "/" + *it, filter, recursive, fast, count ); + } + else if( !fileInfo.isDir() || !recursive ) { // NOTE checking for isFile may not work with all file names + // NOTE filter feature + for( QStringList::Iterator jt = filter.begin(); jt != filter.end(); ++jt ) { + if( (*it).endsWith("."+(*jt),false) ) { + count++; + pScanStatus->setTotalSteps( count ); + break; + } + } + if( filter.first() == "" ) { + count++; + pScanStatus->setTotalSteps( count ); + } + } + } + else { + if( fileInfo.isDir() && recursive ) { + count = listDir( directory + "/" + *it, filter, recursive, fast, count ); + } + else if( !fileInfo.isDir() || !recursive ) { // NOTE checking for isFile may not work with all file names + // NOTE filter feature + for( QStringList::Iterator jt = filter.begin(); jt != filter.end(); ++jt ) { + if( (*it).endsWith("."+(*jt),false) ) { + addFile( KURL::encode_string(directory + "/" + *it) ); + count++; + pScanStatus->setProgress( count ); + break; + } + } + if( filter.first() == "" ) { + addFile( KURL::encode_string(directory + "/" + *it) ); + count++; + pScanStatus->setProgress( count ); + } + } + } + } + + return count; +} + +void ReplayGainFileList::showContextMenu( QListViewItem* item, const QPoint& point, int ) +{ + // remove all items from the context menu + contextMenu->clear(); + + // add a tilte to our context manu + //contextMenu->insertTitle( static_cast<FileListItem*>(item)->fileName ); + + // if item is null, we can abort here + if( item ) { + if( !processing ) { + calc_gain->plug( contextMenu ); + if( item->text(columnByName(i18n("Track"))) != i18n("Unknown") || item->text(columnByName(i18n("Album"))) != i18n("Unknown") ) { + remove_gain->plug( contextMenu ); + } + } + newalbum->plug( contextMenu ); + remove->plug( contextMenu ); + } + else { + newalbum->plug( contextMenu ); + } + paste->plug( contextMenu ); + contextMenu->insertSeparator(); + open_albums->plug( contextMenu ); + close_albums->plug( contextMenu ); + + // show the popup menu + contextMenu->popup( point ); +} + +void ReplayGainFileList::addFile( const QString& file ) +{ + QString filename = file; + QString filePathName; + QString device; + + if( filename.left( 1 ) == "/" ) { + filePathName = filename; + } + else if( filename.left( 7 ) == "file://" ) { + filePathName = filename; + filePathName.remove( 0, 7 ); + } + else if( filename.left( 13 ) == "system:/home/" ) { + filePathName = filename; + filePathName.remove( 0, 13 ); + filePathName = QDir::homeDirPath() + "/" + filePathName; + } + else if( filename.left( 14 ) == "system:/users/" || filename.left( 6 ) == "home:/" ) { + int length = ( filename.left(6) == "home:/" ) ? 6 : 14; + QString username = filename; + username.remove( 0, length ); + username = username.left( username.find("/") ); + filePathName = filename; + filePathName.remove( 0, length + username.length() ); + KUser user( username ); + filePathName = user.homeDir() + filePathName; + } + else if( filename.left( 14 ) == "system:/media/" || filename.left( 7 ) == "media:/" ) { + int length = ( filename.left(7) == "media:/" ) ? 7 : 14; + device = filename; + device.remove( 0, length ); + device = "/dev/" + device.left( device.find( "/" ) ); + + KMountPoint::List mountPoints = KMountPoint::possibleMountPoints(); + + for( KMountPoint::List::ConstIterator jt = mountPoints.begin(); jt != mountPoints.end(); ++jt ) + { + const KSharedPtr<KMountPoint> mp = *jt; + if( mp->mountedFrom() == device ) + { + filePathName = ( mp->mountPoint() == "/" ) ? mp->mountPoint() : mp->mountPoint() + "/"; + filePathName += filename.right( filename.length() - device.length() - length + 4 ); + } + } + } + else { + return; + } + + TagData* tags = tagEngine->readTags( KURL::decode_string(filePathName) ); + if( !tags || tags->album.isEmpty() ) { + ReplayGainFileListItem* item = new ReplayGainFileListItem( this ); + item->originalFileFormat = filePathName.right( filePathName.length() - filePathName.findRev(".") - 1 ); + item->mimeType = KMimeType::findByFileContent( filePathName )->name(); + item->fileFormat = KMimeType::findByFileContent( filePathName )->patterns().first(); + if( item->mimeType.isEmpty() || item->mimeType == "application/octet-stream" || item->mimeType == "text/plain" ) { + item->mimeType = KMimeType::findByURL( filePathName.lower() )->name(); + item->fileFormat = KMimeType::findByURL( filePathName.lower() )->patterns().first(); + } + // check whether the mime type has a decoder registered + if( !config->acceptReplayGainFile( item->mimeType ) ) { + delete item; + return; + } + item->filePathName = filePathName; + if( tags ) item->time = tags->length; + else { + FormatItem* formatItem = config->getFormatItem( item->mimeType ); + if( formatItem && formatItem->size > 0 ) { + QFileInfo fileInfo( filePathName ); + item->time = fileInfo.size() / formatItem->size; + } + else { + item->time = 210; + } + } + item->setText( columnByName(i18n("File")), KURL::decode_string(filePathName).replace("%2f","/").replace("%%","%") ); + if( tags && tags->track_gain != 210588 ) { + item->setText( columnByName(i18n("Track")), QString().sprintf("%+.2f dB",tags->track_gain) ); + } + else { + item->setText( columnByName(i18n("Track")), i18n("Unknown") ); + } + if( tags && tags->album_gain != 210588 ) { + item->setText( columnByName(i18n("Album")), QString().sprintf("%+.2f dB",tags->album_gain) ); + } + else { + item->setText( columnByName(i18n("Album")), i18n("Unknown") ); + } + } + else { + QString mimeType = KMimeType::findByFileContent( filePathName )->name(); + QString fileFormat = KMimeType::findByFileContent( filePathName )->patterns().first(); + if( mimeType.isEmpty() || mimeType == "application/octet-stream" || mimeType == "text/plain" ) { + mimeType = KMimeType::findByURL( filePathName.lower() )->name(); + fileFormat = KMimeType::findByURL( filePathName.lower() )->patterns().first(); + } + // check whether the mime type has a decoder registered + if( !config->acceptReplayGainFile( mimeType ) ) { + return; + } + + for( ReplayGainFileListItem* it = firstChild(); it != 0; it = it->nextSibling() ) { + //if( it->text(0) == QString(tags->artist+" - "+tags->album) ) { + if( it->text(columnByName(i18n("File"))) == tags->album && it->type() == ReplayGainFileListItem::Album && it->mimeType == mimeType ) { + ReplayGainFileListItem* item = new ReplayGainFileListItem( it ); + item->originalFileFormat = filePathName.right( filePathName.length() - filePathName.findRev(".") - 1 ); + item->filePathName = filePathName; + item->mimeType = mimeType; + item->fileFormat = fileFormat; + item->time = tags->length; + item->setText( columnByName(i18n("File")), KURL::decode_string(filePathName).replace("%2f","/").replace("%%","%") ); + if( tags->track_gain != 210588 ) { + item->setText( columnByName(i18n("Track")), QString().sprintf("%+.2f dB",tags->track_gain) ); + } + else { + item->setText( columnByName(i18n("Track")), i18n("Unknown") ); + } + if( tags->album_gain != 210588 ) { + item->setText( columnByName(i18n("Album")), QString().sprintf("%+.2f dB",tags->album_gain) ); + } + else { + item->setText( columnByName(i18n("Album")), i18n("Unknown") ); + } + tags = 0; // <--, + break; // | + } // | + } // | + if( tags ) { // <--' + ReplayGainFileListItem* parent = new ReplayGainFileListItem( this ); + //parent->setText( 0, tags->artist+" - "+tags->album ); + parent->setText( columnByName(i18n("File")), tags->album ); + parent->setType( ReplayGainFileListItem::Album ); + parent->mimeType = mimeType; + parent->fileFormat = fileFormat; + ReplayGainFileListItem* item = new ReplayGainFileListItem( parent ); + item->originalFileFormat = filePathName.right( filePathName.length() - filePathName.findRev(".") - 1 ); + item->filePathName = filePathName; + item->mimeType = mimeType; + item->fileFormat = fileFormat; + item->time = tags->length; + item->setText( columnByName(i18n("File")), KURL::decode_string(filePathName).replace("%2f","/").replace("%%","%") ); + if( tags->track_gain != 210588 ) { + item->setText( columnByName(i18n("Track")), QString().sprintf("%+.2f dB",tags->track_gain) ); + } + else { + item->setText( columnByName(i18n("Track")), i18n("Unknown") ); + } + if( tags->album_gain != 210588 ) { + item->setText( columnByName(i18n("Album")), QString().sprintf("%+.2f dB",tags->album_gain) ); + } + else { + item->setText( columnByName(i18n("Album")), i18n("Unknown") ); + } + } + } +} + +void ReplayGainFileList::addDir( const QString& directory, const QStringList& filter, bool recursive ) +{ + pScanStatus->setProgress( 0 ); + pScanStatus->setTotalSteps( 0 ); + pScanStatus->show(); // show the status while scanning the directories + kapp->processEvents(); + + int count = listDir( directory, filter, recursive, true ); + listDir( directory, filter, recursive ); + + pScanStatus->hide(); // hide the status bar, when the scan is done +} + +void ReplayGainFileList::openAlbums() +{ + // iterate through all items and open all albums + for( ReplayGainFileListItem* item = firstChild(); item != 0; item = item->nextSibling() ) { + if( item->type() == ReplayGainFileListItem::Album ) { + item->setOpen( true ); + } + } +} + +void ReplayGainFileList::closeAlbums() +{ + // iterate through all items and close all albums + for( ReplayGainFileListItem* item = firstChild(); item != 0; item = item->nextSibling() ) { + if( item->type() == ReplayGainFileListItem::Album ) { + item->setOpen( false ); + } + } +} + +void ReplayGainFileList::removeSelectedItems() +{ + ReplayGainFileListItem* i; + + // iterate through all items and remove all selected ones + for( ReplayGainFileListItem* item = firstChild(); item != 0; ) { + if( item->type() == ReplayGainFileListItem::File ) { + if( item->isSelected() ) { + i = item; + item = item->nextSibling(); + delete i; + } + else { + item = item->nextSibling(); + } + } + else { + if( item->isSelected() ) { + i = item; + item = item->nextSibling(); + delete i; + } + else { + for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; ) { + if( sub_item->isSelected() ) { + i = sub_item; + sub_item = sub_item->nextSibling(); + delete i; + } + else { + sub_item = sub_item->nextSibling(); + } + } + if( item->childCount() == 0 ) { + i = item; + item = item->nextSibling(); + delete i; + } + else { + item = item->nextSibling(); + } + } + } + } +} + +void ReplayGainFileList::createNewAlbum() +{ + ReplayGainFileListItem* item = new ReplayGainFileListItem( this ); + item->setText( columnByName(i18n("File")), i18n("New album") ); + item->setType( ReplayGainFileListItem::Album ); + item->mimeType = "application/octet-stream"; +} + +void ReplayGainFileList::calcSelectedItemsGain() +{ + if( processing ) return; + + // iterate through all items and remove the replay gain from all selected ones + for( ReplayGainFileListItem* item = firstChild(); item != 0; item = item->nextSibling() ) { + if( item->type() == ReplayGainFileListItem::File ) { + if( item->isSelected() ) { + item->queued = true; + item->repaint(); + item->mode = ReplayGainFileListItem::force; + } + } + else { + if( item->isSelected() ) { + item->queued = true; + item->repaint(); + item->mode = ReplayGainFileListItem::force; + for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) { + sub_item->queued = true; + sub_item->repaint(); + sub_item->mode = ReplayGainFileListItem::force; + } + } + else { + for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) { + if( sub_item->isSelected() ) { + item->queued = true; + item->repaint(); + item->mode = ReplayGainFileListItem::force; + for( ReplayGainFileListItem* sub_item2 = item->firstChild(); sub_item2 != 0; sub_item2 = sub_item2->nextSibling() ) { + sub_item2->queued = true; + sub_item2->repaint(); + sub_item2->mode = ReplayGainFileListItem::force; + } + break; + } + } + } + } + } + startProcess(); +} + +void ReplayGainFileList::removeSelectedItemsGain() +{ + // iterate through all items and remove the replay gain from all selected ones + for( ReplayGainFileListItem* item = firstChild(); item != 0; item = item->nextSibling() ) { + if( item->type() == ReplayGainFileListItem::File ) { + if( item->isSelected() && !item->addingReplayGain ) { + item->queued = true; + item->repaint(); + item->mode = ReplayGainFileListItem::remove; + } + } + else { + if( item->isSelected() && !item->addingReplayGain ) { + item->queued = true; + item->repaint(); + item->mode = ReplayGainFileListItem::remove; + for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) { + sub_item->queued = true; + sub_item->repaint(); + sub_item->mode = ReplayGainFileListItem::remove; + } + } + else { + for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) { + if( sub_item->isSelected() && !sub_item->addingReplayGain ) { + sub_item->queued = true; + sub_item->repaint(); + sub_item->mode = ReplayGainFileListItem::remove; + } + } + } + } + } + startProcess(); +} + +void ReplayGainFileList::calcReplayGain( ReplayGainFileListItem* item ) +{ + logID = logger->registerProcess( KURL::encode_string(item->text(columnByName(i18n("File")))) ); + logger->log( logID, "Mime Type: " + item->mimeType ); + logger->log( logID, i18n("Applying Replay Gain") ); + + QStringList fileList; + bool force = false; + if( mode & ReplayGainFileListItem::force ) force = true; + QString album_gain = ""; + bool skip = true; + + timeCount = 0; + file = 0; + files = 0; + + if( item->type() == ReplayGainFileListItem::Album ) { + item->queued = false; + item->addingReplayGain = true; + item->repaint(); + for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) { + if( sub_item->queued && sub_item->mode & ReplayGainFileListItem::force ) force = true; // NOTE can this be replaced by checking item? + sub_item->queued = false; + sub_item->addingReplayGain = true; + sub_item->repaint(); + + fileList += sub_item->filePathName; + files++; + timeCount += sub_item->time; + + QString current_gain = sub_item->text( columnByName(i18n("Album")) ); + if( album_gain == "" ) album_gain = current_gain; + else if( album_gain != current_gain ) skip = false; + } + + if( !skip || album_gain == i18n("Unknown") || force ) { + if( force ) { + replayGain->apply( fileList, item->mimeType, process, logID, ReplayGain::Mode(ReplayGain::calc_album|ReplayGain::force) ); + } + else { + replayGain->apply( fileList, item->mimeType, process, logID ); + } + } + else { + logger->processCompleted( logID, 0 ); + item->addingReplayGain = false; + item->repaint(); + for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) { + sub_item->addingReplayGain = false; + sub_item->repaint(); + } + processNextFile(); + } + } + else { + if( item->queued && item->mode & ReplayGainFileListItem::force ) force = true; + + item->queued = false; + item->addingReplayGain = true; + item->repaint(); + + files = 1; + timeCount = item->time; + + if( force ) { + replayGain->apply( item->filePathName, item->mimeType, process, logID, ReplayGain::Mode(ReplayGain::calc_album|ReplayGain::force) ); + } + else { + replayGain->apply( item->filePathName, item->mimeType, process, logID ); + } + } +} + +void ReplayGainFileList::removeReplayGain( ReplayGainFileListItem* item ) +{ + logID = logger->registerProcess( KURL::encode_string(item->text(columnByName(i18n("File")))) ); + logger->log( logID, "Mime Type: " + item->mimeType ); + logger->log( logID, i18n("Removing Replay Gain") ); + + if( item->type() == ReplayGainFileListItem::File ) { + item->queued = false; + item->addingReplayGain = true; + item->repaint(); + timeCount = item->time; + replayGain->apply( item->filePathName, item->mimeType, process, logID, ReplayGain::remove ); + } + else { + item->queued = false; + item->repaint(); + processNextFile(); + } +} + +void ReplayGainFileList::calcAllReplayGain( bool force ) +{ + queue = true; + if( force ) mode = ReplayGainFileListItem::force; + else mode = ReplayGainFileListItem::Mode(0x0000); + startProcess(); +} + +void ReplayGainFileList::removeAllReplayGain() +{ + queue = true; + mode = ReplayGainFileListItem::remove; + startProcess(); +} + +void ReplayGainFileList::cancelProcess() +{ + queue = false; + if( process->isRunning() ) + { + bool ret = process->kill( SIGKILL ); + if( ret ) { + logger->log( logID, i18n("Killing process ...") ); + } + else { + logger->log( logID, i18n("Killing process failed. Stopping after files are completed ...") ); + } + } +} + +void ReplayGainFileList::startProcess() +{ + emit processStarted(); + processing = true; + time = 0; + + for( ReplayGainFileListItem* item = firstChild(); item != 0; item = item->nextSibling() ) { + if( item->type() == ReplayGainFileListItem::File ) { + if( queue ) { + time += item->time; + } + else if( item->queued ) { + time += item->time; + } + } + else { + for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) { + if( queue ) { + time += sub_item->time; + } + else if( sub_item->queued ) { + time += sub_item->time; + } + } + } + } + + emit updateProgress( 0, 100 ); + if( !tUpdateProgress->isActive() ) { + tUpdateProgress->start( 200 ); // TODO use config value + } + + currentItem = 0; + processNextFile(); +} + +void ReplayGainFileList::processNextFile() +{ + percent = 0; + lastPercent = 0; + + ReplayGainFileListItem* currentSubItem = 0; + + if( !currentItem ) { currentItem = firstChild(); } + else if( currentItem->type() == ReplayGainFileListItem::File && currentItem->parent() == 0 ) { currentItem = currentItem->nextSibling(); } + else if( currentItem->type() == ReplayGainFileListItem::Album ) { currentItem = currentItem->nextSibling(); } + else { currentSubItem = currentItem->nextSibling(); currentItem = currentItem->parent(); if( !currentSubItem ) { currentItem = currentItem->nextSibling(); } } + + for( ReplayGainFileListItem* item = currentItem; item != 0; item = item->nextSibling() ) { + if( item->type() == ReplayGainFileListItem::File ) { + if( queue ) { + currentItem = item; + if( mode & ReplayGainFileListItem::remove ) removeReplayGain( item ); + else calcReplayGain( item ); + return; + } + else if( item->queued ) { + currentItem = item; + if( item->mode & ReplayGainFileListItem::remove ) removeReplayGain( item ); + else calcReplayGain( item ); + return; + } + } + else { + if( queue ) { + currentItem = item; + if( mode & ReplayGainFileListItem::remove ) {} + else { calcReplayGain( item ); return; } + } + else if( item->queued ) { + currentItem = item; + if( item->mode & ReplayGainFileListItem::remove ) { item->queued = false; } + else { calcReplayGain( item ); return; } + } + + if( !currentSubItem ) currentSubItem = item->firstChild(); + for( ReplayGainFileListItem* sub_item = currentSubItem; sub_item != 0; sub_item = sub_item->nextSibling() ) { + if( queue ) { + currentItem = sub_item; + if( mode & ReplayGainFileListItem::remove ) removeReplayGain( sub_item ); + return; + } + else if( sub_item->queued ) { + currentItem = sub_item; + if( sub_item->mode & ReplayGainFileListItem::remove ) removeReplayGain( sub_item ); + return; + } + } + + currentSubItem = 0; + } + } + queue = false; + tUpdateProgress->stop(); + processedTime = 0; + processing = false; + emit processStopped(); +} + +void ReplayGainFileList::processOutput( KProcess* proc, char* data, int ) +{ + int iPercent = 0, iTime = 0, iPos = 0, iNum = 0; + + QString log_data = data; + log_data.replace("\n","\\n"); + log_data.replace("\t","\\t"); + log_data.replace("\r","\\r"); + log_data.replace("\b","\\b"); + logger->log( logID, " " + i18n("Output") + ": " + log_data ); + + ReplayGainPlugin* plugin = config->replaygainForFormat( currentItem->mimeType ); + if( plugin == 0 ) { // shouldn't happen + logger->log( logID, " NULL POINTER: ReplayGainScanner::processOutput( ... ) / plugin" ); + return; + } + if( plugin->info.name == i18n("built-in") ) { // shouldn't happen // TODO implement a check for this + logger->log( logID, " Backend is an encoder" ); + return; + } + + QString outputPattern = ( files > 1 ) ? plugin->replaygain.output_multiple : plugin->replaygain.output_single; + //outputPattern.replace( "%i", "%p" ); // for compatibility with old plugins + + if( outputPattern.find("%p") != -1 || outputPattern.find("%a") != -1 ) { + outputPattern.replace( "%p", "%i" ); + //outputPattern.replace( "%a", "%i" ); // for compatibility with old plugins + sscanf( data, outputPattern, &iPercent ); + } + /*else if( outputPattern.find("%t") != -1 ) { // NOTE a little bit complicated and not necessary + outputPattern.replace( "%t", "%i" ); + sscanf( data, outputPattern, &iTime ); + iPercent = iTime * 100 / currentItem->time; + }*/ + else if( outputPattern.find("%0") != -1 && outputPattern.find("%1") != -1 ) { + if( outputPattern.find("%0") < outputPattern.find("%1") ) { + outputPattern.replace( "%0", "%i" ); + outputPattern.replace( "%1", "%i" ); + sscanf( data, outputPattern, &iPos, &iNum ); + } + else { + outputPattern.replace( "%0", "%i" ); + outputPattern.replace( "%1", "%i" ); + sscanf( data, outputPattern, &iNum, &iPos ); + } + if( iPos != 0 && iNum != 0 ) iPercent = iPos * 100 / iNum; + } + + if( iPercent > 0 && iPercent <= 100 ) + { + // TODO guess progress, when no signal is received + //lastOutputTimer.start(); + if( files > 1 ) { + if( iPercent < lastPercent ) file++; + lastPercent = iPercent; + percent = file * 100 / files + iPercent / files; + } + else { + percent = iPercent; + } + } +} + +void ReplayGainFileList::processExit( KProcess* proc ) +{ + logger->processCompleted( logID, ( proc->signalled() ) ? -1 : 0 ); + + for( ReplayGainFileListItem* item = firstChild(); item != 0; item = item->nextSibling() ) { + if( item->type() == ReplayGainFileListItem::File ) { + if( item->addingReplayGain ) { + processedTime += item->time; + item->addingReplayGain = false; + item->repaint(); + item->updateReplayGainCells( tagEngine->readTags(KURL::decode_string(item->filePathName)) ); + } + if( item->queued && proc->signalled() ) { + item->queued = false; + item->repaint(); + } + } + else { + if( item->addingReplayGain ) { + item->addingReplayGain = false; + item->repaint(); + for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) { + processedTime += sub_item->time; + sub_item->addingReplayGain = false; + sub_item->repaint(); + sub_item->updateReplayGainCells( tagEngine->readTags(KURL::decode_string(sub_item->filePathName)) ); + } + } + if( item->queued && proc->signalled() ) { + item->queued = false; + item->repaint(); + for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) { + sub_item->queued = false; + sub_item->repaint(); + } + } + for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) { + if( sub_item->addingReplayGain ) { + processedTime += sub_item->time; + sub_item->addingReplayGain = false; + sub_item->repaint(); + sub_item->updateReplayGainCells( tagEngine->readTags(KURL::decode_string(sub_item->filePathName)) ); + } + if( sub_item->queued && proc->signalled() ) { + sub_item->queued = false; + sub_item->repaint(); + } + } + } + } + if( proc->signalled() ) { + queue = false; + tUpdateProgress->stop(); + processedTime = 0; + processing = false; + emit processStopped(); + return; + } + else { + processNextFile(); + } +} + +void ReplayGainFileList::update() +{ + emit updateProgress( int(processedTime) + percent * int(timeCount) / 100, int(time) ); +} + |