/***************************************************************************
    begin                : Sat Sep 7 2002
    copyright            : (C) 2002 - 2004 by Scott Wheeler
    email                : wheeler@kde.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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "tageditor.h"
#include "collectionlist.h"
#include "playlistitem.h"
#include "tag.h"
#include "actioncollection.h"
#include "tagtransactionmanager.h"

#include <kcombobox.h>
#include <klineedit.h>
#include <knuminput.h>
#include <keditcl.h>
#include <tdemessagebox.h>
#include <tdeconfig.h>
#include <tdelocale.h>
#include <kdebug.h>
#include <kiconloader.h>
#include <tdeactionclasses.h>

#include <tqlabel.h>
#include <tqcheckbox.h>
#include <tqlayout.h>
#include <tqdir.h>
#include <tqvalidator.h>
#include <tqtooltip.h>
#include <tqeventloop.h>
#include <tqdict.h>

#include <id3v1genres.h>

#undef KeyRelease

#define TStringToTQString(s) TQString::fromUtf8((s).toCString(true))

using namespace ActionCollection;

class FileNameValidator : public TQValidator
{
public:
    FileNameValidator(TQObject *parent, const char *name = 0) :
	TQValidator(parent, name) {}

    virtual void fixup(TQString &s) const
    {
	s.remove('/');
    }

    virtual State validate(TQString &s, int &) const
    {
	if(s.find('/') != -1)
	   return Invalid;
	return Acceptable;
    }
};

class FileBoxToolTip : public TQToolTip
{
public:
    FileBoxToolTip(TagEditor *editor, TQWidget *widget) :
	TQToolTip(widget), m_editor(editor) {}
protected:
    virtual void maybeTip(const TQPoint &)
    {
	tip(parentWidget()->rect(), m_editor->items().first()->file().absFilePath());
    }
private:
    TagEditor *m_editor;
};

class FixedHLayout : public TQHBoxLayout
{
public:
    FixedHLayout(TQWidget *parent, int margin = 0, int spacing = -1, const char *name = 0) :
	TQHBoxLayout(parent, margin, spacing, name),
	m_width(-1) {}
    FixedHLayout(TQLayout *parentLayout, int spacing = -1, const char *name = 0) :
	TQHBoxLayout(parentLayout, spacing, name),
	m_width(-1) {}
    void setWidth(int w = -1)
    {
	m_width = w == -1 ? TQHBoxLayout::minimumSize().width() : w;
    }
    virtual TQSize minimumSize() const
    {
	TQSize s = TQHBoxLayout::minimumSize();
	s.setWidth(m_width);
	return s;
    }
private:
    int m_width;
};

class CollectionObserver : public PlaylistObserver
{
public:
    CollectionObserver(TagEditor *parent) :
	PlaylistObserver(CollectionList::instance()),
	m_parent(parent)
    {
    }

    virtual void updateData()
    {
	if(m_parent && m_parent->m_currentPlaylist && m_parent->isVisible())
	    m_parent->slotSetItems(m_parent->m_currentPlaylist->selectedItems());
    }

    virtual void updateCurrent() {}

private:
    TagEditor *m_parent;
};

////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

TagEditor::TagEditor(TQWidget *parent, const char *name) :
    TQWidget(parent, name),
    m_currentPlaylist(0),
    m_observer(0),
    m_performingSave(false)
{
    setupActions();
    setupLayout();
    readConfig();
    m_dataChanged = false;
    m_collectionChanged = false;
}

TagEditor::~TagEditor()
{
    delete m_observer;
    saveConfig();
}

void TagEditor::setupObservers()
{
    m_observer = new CollectionObserver(this);
}

////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////

void TagEditor::slotSetItems(const PlaylistItemList &list)
{
    if(m_performingSave)
	return;

    // Store the playlist that we're setting because saveChangesPrompt
    // can delete the PlaylistItems in list.

    Playlist *itemPlaylist = 0;
    if(!list.isEmpty())
	itemPlaylist = list.first()->playlist();

    bool hadPlaylist = m_currentPlaylist != 0;

    saveChangesPrompt();

    if(m_currentPlaylist) {
	disconnect(m_currentPlaylist, TQ_SIGNAL(signalAboutToRemove(PlaylistItem *)),
		   this, TQ_SLOT(slotItemRemoved(PlaylistItem *)));
    }

    if(hadPlaylist && !m_currentPlaylist || !itemPlaylist) {
	m_currentPlaylist = 0;
	m_items.clear();
    }
    else {
	m_currentPlaylist = itemPlaylist;

	// We can't use list here, it may not be valid

	m_items = itemPlaylist->selectedItems();
    }

    if(m_currentPlaylist) {
	connect(m_currentPlaylist, TQ_SIGNAL(signalAboutToRemove(PlaylistItem *)),
		this, TQ_SLOT(slotItemRemoved(PlaylistItem *)));
	connect(m_currentPlaylist, TQ_SIGNAL(destroyed()), this, TQ_SLOT(slotPlaylistRemoved()));
    }

    if(isVisible())
	slotRefresh();
    else
	m_collectionChanged = true;
}

void TagEditor::slotRefresh()
{
    // This method takes the list of currently selected m_items and tries to 
    // figure out how to show that in the tag editor.  The current strategy --
    // the most common case -- is to just process the first item.  Then we
    // check after that to see if there are other m_items and adjust accordingly.

    if(m_items.isEmpty() || !m_items.first()->file().tag()) {
	slotClear();
	setEnabled(false);
	return;
    }
    
    setEnabled(true);

    PlaylistItem *item = m_items.first();

    Q_ASSERT(item);

    Tag *tag = item->file().tag();

    TQFileInfo fi(item->file().absFilePath());
    if(!fi.isWritable() && m_items.count() == 1)
	setEnabled(false);

    m_artistNameBox->setEditText(tag->artist());
    m_trackNameBox->setText(tag->title());
    m_albumNameBox->setEditText(tag->album());

    m_fileNameBox->setText(item->file().fileInfo().fileName());
    new FileBoxToolTip(this, m_fileNameBox);
    m_bitrateBox->setText(TQString::number(tag->bitrate()));
    m_lengthBox->setText(tag->lengthString());

    if(m_genreList.findIndex(tag->genre()) >= 0)
	m_genreBox->setCurrentItem(m_genreList.findIndex(tag->genre()) + 1);
    else {
	m_genreBox->setCurrentItem(0);
	m_genreBox->setEditText(tag->genre());
    }

    m_trackSpin->setValue(tag->track());
    m_yearSpin->setValue(tag->year());
    
    m_commentBox->setText(tag->comment());
    
    // Start at the second item, since we've already processed the first.
    
    PlaylistItemList::Iterator it = m_items.begin();
    ++it;

    // If there is more than one item in the m_items that we're dealing with...
    
    if(it != m_items.end()) {

	TQValueListIterator<TQWidget *> hideIt = m_hideList.begin();
	for(; hideIt != m_hideList.end(); ++hideIt)
	    (*hideIt)->hide();

	BoxMap::Iterator boxIt = m_enableBoxes.begin();
	for(; boxIt != m_enableBoxes.end(); boxIt++) {
	    (*boxIt)->setChecked(true);
	    (*boxIt)->show();
	}
	
	// Yep, this is ugly.  Loop through all of the files checking to see
	// if their fields are the same.  If so, by default, enable their 
	// checkbox.
	
	// Also, if there are more than 50 m_items, don't scan all of them.
	
	if(m_items.count() > 50) {
	    m_enableBoxes[m_artistNameBox]->setChecked(false);
	    m_enableBoxes[m_trackNameBox]->setChecked(false);
	    m_enableBoxes[m_albumNameBox]->setChecked(false);
	    m_enableBoxes[m_genreBox]->setChecked(false);
	    m_enableBoxes[m_trackSpin]->setChecked(false);
	    m_enableBoxes[m_yearSpin]->setChecked(false);
	    m_enableBoxes[m_commentBox]->setChecked(false);
	}
	else {
	    for(; it != m_items.end(); ++it) {
		tag = (*it)->file().tag();

		if(tag) {

		    if(m_artistNameBox->currentText() != tag->artist() &&
		       m_enableBoxes.contains(m_artistNameBox))
		    {
			m_artistNameBox->lineEdit()->clear();
			m_enableBoxes[m_artistNameBox]->setChecked(false);
		    }
		    if(m_trackNameBox->text() != tag->title() &&
		       m_enableBoxes.contains(m_trackNameBox))
		    {
			m_trackNameBox->clear();
			m_enableBoxes[m_trackNameBox]->setChecked(false);
		    }
		    if(m_albumNameBox->currentText() != tag->album() &&
		       m_enableBoxes.contains(m_albumNameBox))
		    {
			m_albumNameBox->lineEdit()->clear();
			m_enableBoxes[m_albumNameBox]->setChecked(false);
		    }
		    if(m_genreBox->currentText() != tag->genre() &&
		       m_enableBoxes.contains(m_genreBox))
		    {
			m_genreBox->lineEdit()->clear();
			m_enableBoxes[m_genreBox]->setChecked(false);
		    }		
		    if(m_trackSpin->value() != tag->track() &&
		       m_enableBoxes.contains(m_trackSpin))
		    {
			m_trackSpin->setValue(0);
			m_enableBoxes[m_trackSpin]->setChecked(false);
		    }		
		    if(m_yearSpin->value() != tag->year() &&
		       m_enableBoxes.contains(m_yearSpin))
		    {
			m_yearSpin->setValue(0);
			m_enableBoxes[m_yearSpin]->setChecked(false);
		    }
		    if(m_commentBox->text() != tag->comment() &&
		       m_enableBoxes.contains(m_commentBox))
		    {
			m_commentBox->clear();
			m_enableBoxes[m_commentBox]->setChecked(false);
		    }
		}
	    }
	}
    }
    else {
	// Clean up in the case that we are only handling one item.

	TQValueListIterator<TQWidget *> showIt = m_hideList.begin();
	for(; showIt != m_hideList.end(); ++showIt)
	    (*showIt)->show();

	BoxMap::iterator boxIt = m_enableBoxes.begin();
	for(; boxIt != m_enableBoxes.end(); boxIt++) {
	    (*boxIt)->setChecked(true);
	    (*boxIt)->hide();
	}
    }
    m_dataChanged = false;
}

void TagEditor::slotClear()
{
    m_artistNameBox->lineEdit()->clear();
    m_trackNameBox->clear();
    m_albumNameBox->lineEdit()->clear();
    m_genreBox->setCurrentItem(0);
    m_fileNameBox->clear();
    m_trackSpin->setValue(0);
    m_yearSpin->setValue(0);
    m_lengthBox->clear();
    m_bitrateBox->clear();
    m_commentBox->clear();    
}

void TagEditor::slotUpdateCollection()
{
    if(isVisible())
	updateCollection();
    else
	m_collectionChanged = true;
}

void TagEditor::updateCollection()
{
    m_collectionChanged = false;
        
    CollectionList *list = CollectionList::instance();

    if(!list)
	return;
    
    TQStringList artistList = list->uniqueSet(CollectionList::Artists);
    artistList.sort();
    m_artistNameBox->listBox()->clear();
    m_artistNameBox->listBox()->insertStringList(artistList);
    m_artistNameBox->completionObject()->setItems(artistList);

    TQStringList albumList = list->uniqueSet(CollectionList::Albums);
    albumList.sort();
    m_albumNameBox->listBox()->clear();
    m_albumNameBox->listBox()->insertStringList(albumList);
    m_albumNameBox->completionObject()->setItems(albumList);

    // Merge the list of genres found in tags with the standard ID3v1 set.

    StringHash genreHash;

    m_genreList = list->uniqueSet(CollectionList::Genres);

    for(TQStringList::ConstIterator it = m_genreList.begin(); it != m_genreList.end(); ++it)
	genreHash.insert(*it);

    TagLib::StringList genres = TagLib::ID3v1::genreList();

    for(TagLib::StringList::ConstIterator it = genres.begin(); it != genres.end(); ++it)
	genreHash.insert(TStringToTQString((*it)));

    m_genreList = genreHash.values();
    m_genreList.sort();

    m_genreBox->listBox()->clear();
    m_genreBox->listBox()->insertItem(TQString());
    m_genreBox->listBox()->insertStringList(m_genreList);
    m_genreBox->completionObject()->setItems(m_genreList);
}

////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

void TagEditor::readConfig()
{
    // combo box completion modes
	
    TDEConfigGroup config(TDEGlobal::config(), "TagEditor");
    if(m_artistNameBox && m_albumNameBox) {
	readCompletionMode(&config, m_artistNameBox, "ArtistNameBoxMode");
	readCompletionMode(&config, m_albumNameBox, "AlbumNameBoxMode");
	readCompletionMode(&config, m_genreBox, "GenreBoxMode");
    }

    bool show = config.readBoolEntry("Show", false);
    action<TDEToggleAction>("showEditor")->setChecked(show);
    setShown(show);

    TagLib::StringList genres = TagLib::ID3v1::genreList();

    for(TagLib::StringList::ConstIterator it = genres.begin(); it != genres.end(); ++it)
	m_genreList.append(TStringToTQString((*it)));
    m_genreList.sort();

    m_genreBox->clear();
    m_genreBox->insertItem(TQString());
    m_genreBox->insertStringList(m_genreList);
    m_genreBox->completionObject()->setItems(m_genreList);
}

void TagEditor::readCompletionMode(TDEConfigBase *config, KComboBox *box, const TQString &key)
{
    TDEGlobalSettings::Completion mode =
	TDEGlobalSettings::Completion(config->readNumEntry(key, TDEGlobalSettings::CompletionAuto));

    box->setCompletionMode(mode);
}

void TagEditor::saveConfig()
{
    // combo box completion modes

    TDEConfigGroup config(TDEGlobal::config(), "TagEditor");

    if(m_artistNameBox && m_albumNameBox) {
	config.writeEntry("ArtistNameBoxMode", m_artistNameBox->completionMode());
	config.writeEntry("AlbumNameBoxMode", m_albumNameBox->completionMode());
	config.writeEntry("GenreBoxMode", m_genreBox->completionMode());
    }
    config.writeEntry("Show", action<TDEToggleAction>("showEditor")->isChecked());
}

void TagEditor::setupActions()
{
    TDEToggleAction *show = new TDEToggleAction(i18n("Show &Tag Editor"), "edit", 0, ActionCollection::actions(), "showEditor");
    show->setCheckedState(i18n("Hide &Tag Editor"));
    connect(show, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(setShown(bool)));

    new TDEAction(i18n("&Save"), "document-save", "CTRL+t", this, TQ_SLOT(slotSave()), ActionCollection::actions(), "saveItem");
}

void TagEditor::setupLayout()
{
    static const int horizontalSpacing = 12;
    static const int verticalSpacing = 2;

    TQHBoxLayout *layout = new TQHBoxLayout(this, 6, horizontalSpacing);

    //////////////////////////////////////////////////////////////////////////////
    // define two columns of the bottem layout
    //////////////////////////////////////////////////////////////////////////////
    TQVBoxLayout *leftColumnLayout = new TQVBoxLayout(layout, verticalSpacing);
    TQVBoxLayout *rightColumnLayout = new TQVBoxLayout(layout, verticalSpacing);

    layout->setStretchFactor(leftColumnLayout, 2);
    layout->setStretchFactor(rightColumnLayout, 3);

    //////////////////////////////////////////////////////////////////////////////
    // put stuff in the left column -- all of the field names are class wide
    //////////////////////////////////////////////////////////////////////////////
    { // just for organization
    	
	m_artistNameBox = new KComboBox(true, this, "artistNameBox");
	m_artistNameBox->setCompletionMode(TDEGlobalSettings::CompletionAuto);
	addItem(i18n("&Artist name:"), m_artistNameBox, leftColumnLayout, "preferences-desktop-personal");

        m_trackNameBox = new KLineEdit(this, "trackNameBox");
	addItem(i18n("&Track name:"), m_trackNameBox, leftColumnLayout, "media-playback-start");

	m_albumNameBox = new KComboBox(true, this, "albumNameBox");
	m_albumNameBox->setCompletionMode(TDEGlobalSettings::CompletionAuto);
	addItem(i18n("Album &name:"), m_albumNameBox, leftColumnLayout, "media-optical-cdrom-unmounted");

        m_genreBox = new KComboBox(true, this, "genreBox");
	addItem(i18n("&Genre:"), m_genreBox, leftColumnLayout, "knotify");

        // this fills the space at the bottem of the left column
        leftColumnLayout->addItem(new TQSpacerItem(0, 0, TQSizePolicy::Minimum,
						  TQSizePolicy::Expanding));
    }
    //////////////////////////////////////////////////////////////////////////////
    // put stuff in the right column
    //////////////////////////////////////////////////////////////////////////////
    { // just for organization
	
	TQHBoxLayout *fileNameLayout = new TQHBoxLayout(rightColumnLayout,
						      horizontalSpacing);

	m_fileNameBox = new KLineEdit(this, "fileNameBox");
	m_fileNameBox->setValidator(new FileNameValidator(m_fileNameBox));	

	TQLabel *fileNameIcon = new TQLabel(this);
	fileNameIcon->setPixmap(SmallIcon("audio-x-generic"));
	TQWidget *fileNameLabel = addHidden(new TQLabel(m_fileNameBox, i18n("&File name:"), this));

	fileNameLayout->addWidget(addHidden(fileNameIcon));
	fileNameLayout->addWidget(fileNameLabel);
	fileNameLayout->setStretchFactor(fileNameIcon, 0);
	fileNameLayout->setStretchFactor(fileNameLabel, 0);
	fileNameLayout->insertStretch(-1, 1);
	rightColumnLayout->addWidget(addHidden(m_fileNameBox));

        { // lay out the track row
            FixedHLayout *trackRowLayout = new FixedHLayout(rightColumnLayout,
							    horizontalSpacing);

	    m_trackSpin = new KIntSpinBox(0, 9999, 1, 0, 10, this, "trackSpin");
	    addItem(i18n("T&rack:"), m_trackSpin, trackRowLayout);
	    m_trackSpin->installEventFilter(this);

	    trackRowLayout->addItem(new TQSpacerItem(0, 0, TQSizePolicy::Expanding,
						    TQSizePolicy::Minimum));

	    m_yearSpin = new KIntSpinBox(0, 9999, 1, 0, 10, this, "yearSpin");
	    addItem(i18n("&Year:"), m_yearSpin, trackRowLayout);
	    m_yearSpin->installEventFilter(this);

	    trackRowLayout->addItem(new TQSpacerItem(0, 0, TQSizePolicy::Expanding,
						    TQSizePolicy::Minimum));

	    trackRowLayout->addWidget(addHidden(new TQLabel(i18n("Length:"), this)));
	    m_lengthBox = new KLineEdit(this, "lengthBox");
	    // addItem(i18n("Length:"), m_lengthBox, trackRowLayout);
	    m_lengthBox->setMinimumWidth(fontMetrics().width("00:00") + trackRowLayout->spacing());
	    m_lengthBox->setMaximumWidth(50);
	    m_lengthBox->setAlignment(TQt::AlignRight);
	    m_lengthBox->setReadOnly(true);
	    trackRowLayout->addWidget(addHidden(m_lengthBox));

	    trackRowLayout->addItem(new TQSpacerItem(0, 0, TQSizePolicy::Expanding,
						    TQSizePolicy::Minimum));

	    trackRowLayout->addWidget(addHidden(new TQLabel(i18n("Bitrate:"), this)));
	    m_bitrateBox = new KLineEdit(this, "bitrateBox");
	    // addItem(i18n("Bitrate:"), m_bitrateBox, trackRowLayout);
	    m_bitrateBox->setMinimumWidth(fontMetrics().width("000") + trackRowLayout->spacing());
	    m_bitrateBox->setMaximumWidth(50);
	    m_bitrateBox->setAlignment(TQt::AlignRight);
	    m_bitrateBox->setReadOnly(true);
	    trackRowLayout->addWidget(addHidden(m_bitrateBox));

	    trackRowLayout->setWidth();
        }

        m_commentBox = new KEdit(this, "commentBox");
	m_commentBox->setTextFormat(TQt::PlainText);
	addItem(i18n("&Comment:"), m_commentBox, rightColumnLayout, "edit");
    	fileNameLabel->setMinimumHeight(m_trackSpin->height());

    }

    connect(m_artistNameBox, TQ_SIGNAL(textChanged(const TQString&)),
	    this, TQ_SLOT(slotDataChanged()));

    connect(m_trackNameBox, TQ_SIGNAL(textChanged(const TQString&)),
	    this, TQ_SLOT(slotDataChanged()));

    connect(m_albumNameBox, TQ_SIGNAL(textChanged(const TQString&)),
	    this, TQ_SLOT(slotDataChanged()));

    connect(m_genreBox, TQ_SIGNAL(activated(int)),
	    this, TQ_SLOT(slotDataChanged()));

    connect(m_genreBox, TQ_SIGNAL(textChanged(const TQString&)),
	    this, TQ_SLOT(slotDataChanged()));

    connect(m_fileNameBox, TQ_SIGNAL(textChanged(const TQString&)),
	    this, TQ_SLOT(slotDataChanged()));

    connect(m_yearSpin, TQ_SIGNAL(valueChanged(int)),
	    this, TQ_SLOT(slotDataChanged()));

    connect(m_trackSpin, TQ_SIGNAL(valueChanged(int)),
	    this, TQ_SLOT(slotDataChanged()));

    connect(m_commentBox, TQ_SIGNAL(textChanged()),
	    this, TQ_SLOT(slotDataChanged()));
}

void TagEditor::save(const PlaylistItemList &list)
{
    if(!list.isEmpty() && m_dataChanged) {
	
	TDEApplication::setOverrideCursor(TQt::waitCursor);
	m_dataChanged = false;
	m_performingSave = true;

	// The list variable can become corrupted if the playlist holding its
	// items dies, which is possible as we edit tags.  So we need to copy
	// the end marker.

	PlaylistItemList::ConstIterator end = list.end();
	
	for(PlaylistItemList::ConstIterator it = list.begin(); it != end; /* Deliberatly missing */ ) {

	    // Process items before we being modifying tags, as the dynamic
	    // playlists will try to modify the file we edit if the tag changes
	    // due to our alterations here.

	    kapp->eventLoop()->processEvents(TQEventLoop::ExcludeUserInput);

	    PlaylistItem *item = *it;
	    
	    // The playlist can be deleted from under us if this is the last
	    // item and we edit it so that it doesn't match the search, which
	    // means we can't increment the iterator, so let's do it now.

	    ++it;

	    TQString fileName = item->file().fileInfo().dirPath() + TQDir::separator() +
	                       m_fileNameBox->text();
	    if(list.count() > 1)
		fileName = item->file().fileInfo().absFilePath();

	    Tag *tag = TagTransactionManager::duplicateTag(item->file().tag(), fileName);
		
	    // A bit more ugliness.  If there are multiple files that are
	    // being modified, they each have a "enabled" checkbox that
	    // says if that field is to be respected for the multiple 
	    // files.  We have to check to see if that is enabled before
	    // each field that we write.
	    
	    if(m_enableBoxes[m_artistNameBox]->isOn())
		tag->setArtist(m_artistNameBox->currentText());
	    if(m_enableBoxes[m_trackNameBox]->isOn())
		tag->setTitle(m_trackNameBox->text());
	    if(m_enableBoxes[m_albumNameBox]->isOn())
		tag->setAlbum(m_albumNameBox->currentText());
	    if(m_enableBoxes[m_trackSpin]->isOn()) {
		if(m_trackSpin->text().isEmpty())
		    m_trackSpin->setValue(0);
		tag->setTrack(m_trackSpin->value());
	    }
	    if(m_enableBoxes[m_yearSpin]->isOn()) {
		if(m_yearSpin->text().isEmpty())
		    m_yearSpin->setValue(0);
		tag->setYear(m_yearSpin->value());
	    }
	    if(m_enableBoxes[m_commentBox]->isOn())
		tag->setComment(m_commentBox->text());
	    
	    if(m_enableBoxes[m_genreBox]->isOn())
		tag->setGenre(m_genreBox->currentText());

	    TagTransactionManager::instance()->changeTagOnItem(item, tag);
	}
	
	TagTransactionManager::instance()->commit();
	CollectionList::instance()->dataChanged();
	m_performingSave = false;
	TDEApplication::restoreOverrideCursor();
    }
}

void TagEditor::saveChangesPrompt()
{
    if(!isVisible() || !m_dataChanged || m_items.isEmpty())
	return;

    TQStringList files;

    for(PlaylistItemList::Iterator it = m_items.begin(); it != m_items.end(); ++it)
	files.append((*it)->file().absFilePath());

    if(KMessageBox::questionYesNoList(this,
				      i18n("Do you want to save your changes to:\n"), 
				      files, 
				      i18n("Save Changes"),
				      KStdGuiItem::save(),
				      KStdGuiItem::discard(),
				      "tagEditor_showSaveChangesBox") == KMessageBox::Yes)
    {
	save(m_items);
    }
}

void TagEditor::addItem(const TQString &text, TQWidget *item, TQBoxLayout *layout, const TQString &iconName)
{
    if(!item || !layout)
	return;

    TQLabel *label = new TQLabel(item, text, this);
    TQLabel *iconLabel = new TQLabel(item, 0, this);
    
    if(!iconName.isNull())
	iconLabel->setPixmap(SmallIcon(iconName));
    
    TQCheckBox *enableBox = new TQCheckBox(i18n("Enable"), this);
    enableBox->setChecked(true);

    label->setMinimumHeight(enableBox->height());

    if(layout->direction() == TQBoxLayout::LeftToRight) {
    	layout->addWidget(iconLabel);
	layout->addWidget(label);
	layout->addWidget(item);
	layout->addWidget(enableBox);
    }
    else {
	TQHBoxLayout *l = new TQHBoxLayout(layout);
	
	l->addWidget(iconLabel);
	l->addWidget(label);
	l->setStretchFactor(label, 1);

	l->insertStretch(-1, 1);

	l->addWidget(enableBox);
	l->setStretchFactor(enableBox, 0);

	layout->addWidget(item);
    }

    enableBox->hide();

    connect(enableBox, TQ_SIGNAL(toggled(bool)), item, TQ_SLOT(setEnabled(bool)));
    m_enableBoxes.insert(item, enableBox);
}

void TagEditor::showEvent(TQShowEvent *e)
{
    if(m_collectionChanged) {
	updateCollection();
	slotRefresh();
    }

    TQWidget::showEvent(e);
}

bool TagEditor::eventFilter(TQObject *watched, TQEvent *e)
{
    TQKeyEvent *ke = static_cast<TQKeyEvent*>(e);
    if(watched->inherits("TQSpinBox") && e->type() == TQEvent::KeyRelease && ke->state() == 0)
	slotDataChanged();

    return false;
}

////////////////////////////////////////////////////////////////////////////////
// private slots
////////////////////////////////////////////////////////////////////////////////

void TagEditor::slotDataChanged(bool c)
{
    m_dataChanged = c;
}

void TagEditor::slotItemRemoved(PlaylistItem *item)
{
    m_items.remove(item);
    if(m_items.isEmpty())
	slotRefresh();
}

void TagEditor::slotPlaylistDestroyed(Playlist *p)
{
    if(m_currentPlaylist == p) {
	m_currentPlaylist = 0;
	slotSetItems(PlaylistItemList());
    }
}

#include "tageditor.moc"
