#include "replaygainfilelist.h" #include "tagengine.h" #include "logger.h" #include "config.h" #include "replaygain.h" #include "replaygainpluginloader.h" #include <tqdir.h> #include <tqpainter.h> #include <tqsimplerichtext.h> #include <tqapplication.h> #include <tqheader.h> #include <tqlayout.h> #include <tqtimer.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( TQListView* parent ) : KListViewItem( parent ) { m_type = File; mimeType = "application/octet-stream"; addingReplayGain = false; queued = false; } // ReplayGainFileListItem::ReplayGainFileListItem( TQListView* parent, TQListViewItem* 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( TQPainter *p, const TQColorGroup &cg, int column, int width, int alignment ) { // NOTE speed up this function // NOTE calculate the red color TQColorGroup _cg( cg ); TQColor c; if( column == ((ReplayGainFileList*)listView())->columnByName(i18n("File")) ) { int margin = listView()->itemMargin(); int w = width - 2*margin; int h = height(); TQRect textRect = p->boundingRect( margin, 0, w, h, alignment, text(column) ); if( textRect.width() > w ) { alignment = TQt::AlignRight | TQt::SingleLine; } } if( isSelected() && addingReplayGain ) { _cg.setColor( TQColorGroup::Highlight, TQColor( 215, 62, 62 ) ); TQListViewItem::paintCell( p, _cg, column, width, alignment ); return; } else if( addingReplayGain && column != listView()->sortColumn() ) { _cg.setColor( TQColorGroup::Base, TQColor( 255, 234, 234 ) ); TQListViewItem::paintCell( p, _cg, column, width, alignment ); return; } else if( addingReplayGain && column == listView()->sortColumn() ) { _cg.setColor( TQColorGroup::Base, TQColor( 247, 227, 227 ) ); TQListViewItem::paintCell( p, _cg, column, width, alignment ); return; } if( isSelected() && queued ) { _cg.setColor( TQColorGroup::Highlight, TQColor( 230, 232, 100 ) ); TQListViewItem::paintCell( p, _cg, column, width, alignment ); return; } else if( queued && column != listView()->sortColumn() ) { _cg.setColor( TQColorGroup::Base, TQColor( 255, 255, 190 ) ); TQListViewItem::paintCell( p, _cg, column, width, alignment ); return; } else if( queued && column == listView()->sortColumn() ) { _cg.setColor( TQColorGroup::Base, TQColor( 255, 243, 168 ) ); TQListViewItem::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")), TQString().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")), TQString().sprintf("%+.2f dB",tags->album_gain) ); } else { setText( ((ReplayGainFileList*)listView())->columnByName(i18n("Album")), i18n("Unknown") ); } } } int ReplayGainFileListItem::compare( TQListViewItem* 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, TQWidget *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, TQListView::Manual ); addColumn( i18n("Track"), 90 ); setColumnAlignment( 1, TQt::AlignRight ); addColumn( i18n("Album"), 90 ); setColumnAlignment( 2, TQt::AlignRight ); setSelectionMode( TQListView::Extended ); setAllColumnsShowFocus( true ); setResizeMode( TQListView::LastColumn ); setShowSortIndicator( true ); setSorting( 0 ); setRootIsDecorated( true ); setDragEnabled( true ); setAcceptDrops( true ); TQGridLayout* grid = new TQGridLayout( 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( TQT_TQOBJECT(this), TQT_SIGNAL(contextMenuRequested(TQListViewItem*,const TQPoint&,int)), TQT_TQOBJECT(this), TQT_SLOT(showContextMenu(TQListViewItem*,const TQPoint&,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, TQT_TQOBJECT(this), TQT_SLOT(calcSelectedItemsGain()), actionCollection, "calc_album" ); remove_gain = new KAction( i18n("Remove Replay Gain tags"), "cancel", 0, TQT_TQOBJECT(this), TQT_SLOT(removeSelectedItemsGain()), actionCollection, "remove_gain" ); remove = new KAction( i18n("Remove"), "edittrash", Key_Delete, TQT_TQOBJECT(this), TQT_SLOT(removeSelectedItems()), actionCollection, "remove" ); paste = new KAction( i18n("Paste"), "editpaste", 0, TQT_TQOBJECT(this), 0, actionCollection, "paste" ); newalbum = new KAction( i18n("New album"), "filenew", 0, TQT_TQOBJECT(this), TQT_SLOT(createNewAlbum()), actionCollection, "newalbum" ); open_albums = new KAction( i18n("Open all albums"), "view_tree", 0, TQT_TQOBJECT(this), TQT_SLOT(openAlbums()), actionCollection, "open_albums" ); close_albums = new KAction( i18n("Cloase all albums"), "view_text", 0, TQT_TQOBJECT(this), TQT_SLOT(closeAlbums()), actionCollection, "close_albums" ); replayGain = new ReplayGain( config, logger ); bubble = new TQSimpleRichText( 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>" ), TQApplication::font() ); connect( header(), TQT_SIGNAL(sizeChange( int, int, int )), TQT_SLOT(columnResizeEvent( int, int, int )) ); connect( TQT_TQOBJECT(this), TQT_SIGNAL( dropped(TQDropEvent*, TQListViewItem*, TQListViewItem*) ), TQT_SLOT( slotDropped(TQDropEvent*, TQListViewItem*, TQListViewItem*) ) ); process = new KProcess(); connect( process, TQT_SIGNAL(receivedStdout(KProcess*,char*,int)), TQT_TQOBJECT(this), TQT_SLOT(processOutput(KProcess*,char*,int)) ); connect( process, TQT_SIGNAL(receivedStderr(KProcess*,char*,int)), TQT_TQOBJECT(this), TQT_SLOT(processOutput(KProcess*,char*,int)) ); connect( process, TQT_SIGNAL(processExited(KProcess*)), TQT_TQOBJECT(this), TQT_SLOT(processExit(KProcess*)) ); tUpdateProgress = new TQTimer( this, "tUpdateProgress" ); connect( tUpdateProgress, TQT_SIGNAL(timeout()), TQT_TQOBJECT(this), TQT_SLOT(update()) ); } ReplayGainFileList::~ReplayGainFileList() { delete replayGain; } int ReplayGainFileList::columnByName( const TQString& name ) { for( int i = 0; i < columns(); ++i ) { if( columnText( i ) == name ) return i; } return -1; } void ReplayGainFileList::viewportPaintEvent( TQPaintEvent* e ) { KListView::viewportPaintEvent( e ); // the bubble help if( childCount() == 0 ) { TQPainter 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, TQRect(), colorGroup() ); } } void ReplayGainFileList::viewportResizeEvent( TQResizeEvent* ) { // 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( TQDropEvent* e ) const { return ( e->source() == viewport() || KURLDrag::canDecode(e) ); // TODO verify the files } void ReplayGainFileList::slotDropped( TQDropEvent* e, TQListViewItem*, TQListViewItem* ) { TQString 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 = TQDir::convertSeparators( (*it).pathOrURL() ); TQFileInfo fileInfo( file ); if( fileInfo.isDir() ) { addDir( file ); } else { addFile( (*it).url() ); } } } } void ReplayGainFileList::contentsDragEnterEvent( TQDragEnterEvent *e ) { e->accept( e->source() == viewport() || KURLDrag::canDecode(e) ); } void ReplayGainFileList::contentsDragMoveEvent( TQDragMoveEvent *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 TQPoint 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( TQDropEvent *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 TQPoint 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() ) { TQPoint 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 TQString& directory, TQStringList filter, bool recursive, bool fast, int count ) { // NOTE speed up? TQDir dir( directory ); dir.setFilter( TQDir::Files | TQDir::Dirs | TQDir::NoSymLinks | TQDir::Readable ); TQStringList list = dir.entryList(); for( TQStringList::Iterator it = list.begin(); it != list.end(); ++it ) { if( *it == "." || *it == ".." ) continue; TQFileInfo 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( TQStringList::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( TQStringList::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( TQListViewItem* item, const TQPoint& 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 TQString& file ) { TQString filename = file; TQString filePathName; TQString 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 = TQDir::homeDirPath() + "/" + filePathName; } else if( filename.left( 14 ) == "system:/users/" || filename.left( 6 ) == "home:/" ) { int length = ( filename.left(6) == "home:/" ) ? 6 : 14; TQString 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 ) { TQFileInfo 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")), TQString().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")), TQString().sprintf("%+.2f dB",tags->album_gain) ); } else { item->setText( columnByName(i18n("Album")), i18n("Unknown") ); } } else { TQString mimeType = KMimeType::findByFileContent( filePathName )->name(); TQString 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) == TQString(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")), TQString().sprintf("%+.2f dB",tags->track_gain) ); } else { item->setText( columnByName(i18n("Track")), i18n("Unknown") ); } if( tags->album_gain != 210588 ) { item->setText( columnByName(i18n("Album")), TQString().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")), TQString().sprintf("%+.2f dB",tags->track_gain) ); } else { item->setText( columnByName(i18n("Track")), i18n("Unknown") ); } if( tags->album_gain != 210588 ) { item->setText( columnByName(i18n("Album")), TQString().sprintf("%+.2f dB",tags->album_gain) ); } else { item->setText( columnByName(i18n("Album")), i18n("Unknown") ); } } } } void ReplayGainFileList::addDir( const TQString& directory, const TQStringList& 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") ); TQStringList fileList; bool force = false; if( mode & ReplayGainFileListItem::force ) force = true; TQString 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; TQString 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; TQString 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; } TQString 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) ); }