tageditor.cpp 20.2 KB
Newer Older
1 2 3 4
/***************************************************************************
                          tageditor.cpp  -  description
                             -------------------
    begin                : Sat Sep 7 2002
5
    copyright            : (C) 2002, 2003 by Scott Wheeler
6
    email                : wheeler@kde.org
7 8 9 10 11 12 13 14 15 16 17
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

18 19 20 21 22
#include "tageditor.h"
#include "collectionlist.h"
#include "playlistitem.h"
#include "tag.h"

23 24 25 26
#include <kcombobox.h>
#include <klineedit.h>
#include <knuminput.h>
#include <keditcl.h>
27
#include <kmessagebox.h>
28
#include <kconfig.h>
29 30
#include <klocale.h>
#include <kdebug.h>
31
#include <kiconloader.h>
32 33

#include <qlabel.h>
34
#include <qcheckbox.h>
35 36
#include <qlayout.h>
#include <qdir.h>
Scott Wheeler's avatar
Scott Wheeler committed
37
#include <qvalidator.h>
38

39 40
#include <id3v1genres.h>

Scott Wheeler's avatar
Scott Wheeler committed
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
class FileNameValidator : public QValidator
{
public:
    FileNameValidator(QObject *parent, const char *name = 0) :
	QValidator(parent, name) {}

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

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

60 61 62 63
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

64 65
TagEditor::TagEditor(QWidget *parent, const char *name) : QWidget(parent, name),
							  m_currentPlaylist(0)
66 67 68
{
    setupLayout();
    readConfig();
69
    m_dataChanged = false;
70 71 72 73 74 75 76 77 78 79 80
}

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

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

81
void TagEditor::slotSetItems(const PlaylistItemList &list)
82 83
{
    saveChangesPrompt();
84 85 86 87 88 89 90 91 92 93 94 95 96

    if(m_currentPlaylist) {
	disconnect(m_currentPlaylist, SIGNAL(signalAboutToRemove(PlaylistItem *)),
		   this, SLOT(slotItemRemoved(PlaylistItem *)));
    }

    m_currentPlaylist = list.isEmpty() ? 0 : static_cast<Playlist *>(list.first()->listView());

    if(m_currentPlaylist) {
	connect(m_currentPlaylist, SIGNAL(signalAboutToRemove(PlaylistItem *)),
		this, SLOT(slotItemRemoved(PlaylistItem *)));
    }

97
    m_items = list;
98

99
    if(isVisible())
100
	slotRefresh();
101 102
}

103
void TagEditor::slotRefresh()
104
{
105
    // This method takes the list of currently selected m_items and tries to 
106 107
    // 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
108
    // check after that to see if there are other m_items and adjust accordingly.
109

110
    if(m_items.isEmpty() || !m_items.first()->file().tag()) {
111
	slotClear();
112
	setEnabled(false);
113 114 115
	return;
    }
    
116 117
    setEnabled(true);

118
    PlaylistItem *item = m_items.first();
119

120
    Tag *tag = item->file().tag();
121
	
122
    m_artistNameBox->setEditText(tag->artist());
123
    m_trackNameBox->setText(tag->title());
124
    m_albumNameBox->setEditText(tag->album());
125

126
    m_fileNameBox->setText(item->file().fileInfo().fileName());
127
    m_bitrateBox->setText(QString::number(tag->bitrate()));
128 129
    m_lengthBox->setText(tag->lengthString());

130 131
    if(m_genreList.findIndex(tag->genre()) >= 0)
	m_genreBox->setCurrentItem(m_genreList.findIndex(tag->genre()) + 1);
132 133
    else {
	m_genreBox->setCurrentItem(0);
134
	m_genreBox->setEditText(tag->genre());
135
    }
136

137
    m_trackSpin->setValue(tag->track());
138 139 140 141 142 143 144 145 146 147 148 149
    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()) {
150 151 152 153

	QValueListIterator<QWidget *> hideIt = m_hideList.begin();
	for(; hideIt != m_hideList.end(); ++hideIt)
	    (*hideIt)->hide();
Scott Wheeler's avatar
Scott Wheeler committed
154 155 156

	BoxMap::Iterator boxIt = m_enableBoxes.begin();
	for(; boxIt != m_enableBoxes.end(); boxIt++) {
157 158
	    (*boxIt)->setChecked(true);
	    (*boxIt)->show();
159 160
	}
	
161 162 163
	// 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.
164
	
165
	// Also, if there are more than 50 m_items, don't scan all of them.
166
	
167 168 169 170 171 172 173 174
	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);
175 176
	}
	else {
177
	    for(; it != m_items.end(); ++it) {
178
		tag = (*it)->file().tag();
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223

		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);
		    }
224
		}
225 226
	    }
	}
227
    }
228 229
    else {
	// Clean up in the case that we are only handling one item.
230 231 232 233

	QValueListIterator<QWidget *> showIt = m_hideList.begin();
	for(; showIt != m_hideList.end(); ++showIt)
	    (*showIt)->show();
Scott Wheeler's avatar
Scott Wheeler committed
234 235 236

	BoxMap::iterator boxIt = m_enableBoxes.begin();
	for(; boxIt != m_enableBoxes.end(); boxIt++) {
237 238 239 240 241
	    (*boxIt)->setChecked(true);
	    (*boxIt)->hide();
	}
    }
    m_dataChanged = false;
242 243
}

244
void TagEditor::slotClear()
245
{
246 247 248 249 250 251 252 253 254 255
    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();    
256 257
}

258
void TagEditor::slotUpdateCollection()
259
{
260 261 262 263 264
    CollectionList *list = CollectionList::instance();

    if(!list)
	return;
    
265 266 267 268 269 270 271 272 273
    QStringList artistList = list->uniqueSet(CollectionList::Artists);
    m_artistNameBox->listBox()->clear();
    m_artistNameBox->listBox()->insertStringList(artistList);
    m_artistNameBox->completionObject()->setItems(artistList);

    QStringList albumList = list->uniqueSet(CollectionList::Albums);
    m_albumNameBox->listBox()->clear();
    m_albumNameBox->listBox()->insertStringList(albumList);
    m_albumNameBox->completionObject()->setItems(albumList);
274 275 276 277 278 279
}

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

280
void TagEditor::readConfig()
281
{
282 283
    KConfig *config = KGlobal::config();
    { // combo box completion modes
284 285
	KConfigGroupSaver saver(config, "TagEditor");
	if(m_artistNameBox && m_albumNameBox) {
286 287 288
	    readCompletionMode(config, m_artistNameBox, "ArtistNameBoxMode");
	    readCompletionMode(config, m_albumNameBox, "AlbumNameBoxMode");
	    readCompletionMode(config, m_genreBox, "GenreBoxMode");
289 290
        }
    }
291

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

294 295 296
    for(TagLib::StringList::ConstIterator it = genres.begin(); it != genres.end(); ++it)
	m_genreList.append(TStringToQString((*it)));
    m_genreList.sort();
297

298 299 300
    m_genreBox->clear();
    m_genreBox->insertItem(QString::null);
    m_genreBox->insertStringList(m_genreList);
301 302 303 304 305 306 307 308 309
    m_genreBox->completionObject()->setItems(m_genreList);
}

void TagEditor::readCompletionMode(KConfig *config, KComboBox *box, const QString &key)
{
    KGlobalSettings::Completion mode =
	KGlobalSettings::Completion(config->readNumEntry(key, KGlobalSettings::CompletionAuto));

    box->setCompletionMode(mode);
310
}
311

312 313 314 315 316
void TagEditor::saveConfig()
{
    KConfig *config = KGlobal::config();
    { // combo box completion modes
        KConfigGroupSaver saver(config, "TagEditor");
317 318 319
        if(m_artistNameBox && m_albumNameBox) {
	    config->writeEntry("ArtistNameBoxMode", m_artistNameBox->completionMode());
	    config->writeEntry("AlbumNameBoxMode", m_albumNameBox->completionMode());
320
	    config->writeEntry("GenreBoxMode", m_genreBox->completionMode());
321 322
        }
    }
323

324
}
325

326 327
void TagEditor::setupLayout()
{
328
    static const int horizontalSpacing = 12;
329
    static const int verticalSpacing = 2;
330

331
    QHBoxLayout *layout = new QHBoxLayout(this, 6, horizontalSpacing);
332 333 334 335 336 337 338 339 340 341 342 343 344 345

    //////////////////////////////////////////////////////////////////////////////
    // define two columns of the bottem layout
    //////////////////////////////////////////////////////////////////////////////
    QVBoxLayout *leftColumnLayout = new QVBoxLayout(layout, verticalSpacing);
    QVBoxLayout *rightColumnLayout = new QVBoxLayout(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
346 347
    	
	m_artistNameBox = new KComboBox(true, this, "artistNameBox");
348
	m_artistNameBox->setCompletionMode(KGlobalSettings::CompletionAuto);
349
	addItem(i18n("&Artist name:"), m_artistNameBox, leftColumnLayout, "personal");
350

351
        m_trackNameBox = new KLineEdit(this, "trackNameBox");
352
	addItem(i18n("&Track name:"), m_trackNameBox, leftColumnLayout, "player_play");
353

354
	m_albumNameBox = new KComboBox(true, this, "albumNameBox");
355
	m_albumNameBox->setCompletionMode(KGlobalSettings::CompletionAuto);
356
	addItem(i18n("Album &name:"), m_albumNameBox, leftColumnLayout, "cdrom_unmount");
357

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

        // this fills the space at the bottem of the left column
Scott Wheeler's avatar
Scott Wheeler committed
362 363
        leftColumnLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum,
						  QSizePolicy::Expanding));
364 365 366 367 368
    }
    //////////////////////////////////////////////////////////////////////////////
    // put stuff in the right column
    //////////////////////////////////////////////////////////////////////////////
    { // just for organization
369 370 371
	
	QHBoxLayout *fileNameLayout = new QHBoxLayout(rightColumnLayout,
							horizontalSpacing);
372

373
	m_fileNameBox = new KLineEdit(this, "fileNameBox");
Scott Wheeler's avatar
Scott Wheeler committed
374 375
	m_fileNameBox->setValidator(new FileNameValidator(m_fileNameBox));	

376 377
	QLabel *fileNameIcon = new QLabel(this);
	fileNameIcon->setPixmap(SmallIcon("sound"));
Scott Wheeler's avatar
Scott Wheeler committed
378 379
	QWidget *fileNameLabel = addHidden(new QLabel(m_fileNameBox, i18n("&File name:"), this));

380 381 382 383 384 385
	fileNameLayout->addWidget(addHidden(fileNameIcon));
	fileNameLayout->addWidget(fileNameLabel);
	fileNameLayout->setStretchFactor(fileNameIcon, 0);
	fileNameLayout->setStretchFactor(fileNameLabel, 0);
	fileNameLayout->insertStretch(-1, 1);
	rightColumnLayout->addWidget(addHidden(m_fileNameBox));
386 387

        { // lay out the track row
Scott Wheeler's avatar
Scott Wheeler committed
388 389
            QHBoxLayout *trackRowLayout = new QHBoxLayout(rightColumnLayout,
							  horizontalSpacing);
390

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

395
	    trackRowLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding,
Scott Wheeler's avatar
Scott Wheeler committed
396
						    QSizePolicy::Minimum));
397

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

402
	    trackRowLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding,
Scott Wheeler's avatar
Scott Wheeler committed
403
						    QSizePolicy::Minimum));
404

405 406 407 408 409 410 411
	    trackRowLayout->addWidget(addHidden(new QLabel(i18n("Length:"), this)));
	    m_lengthBox = new KLineEdit(this, "lengthBox");
	    // addItem(i18n("Length:"), m_lengthBox, trackRowLayout);
	    m_lengthBox->setMaximumWidth(50);
	    m_lengthBox->setAlignment(Qt::AlignRight);
	    m_lengthBox->setReadOnly(true);
	    trackRowLayout->addWidget(addHidden(m_lengthBox));
412

413
	    trackRowLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding,
Scott Wheeler's avatar
Scott Wheeler committed
414
						    QSizePolicy::Minimum));
415

416 417 418 419 420 421 422
	    trackRowLayout->addWidget(addHidden(new QLabel(i18n("Bitrate:"), this)));
	    m_bitrateBox = new KLineEdit(this, "bitrateBox");
	    // addItem(i18n("Bitrate:"), m_bitrateBox, trackRowLayout);
	    m_bitrateBox->setMaximumWidth(50);
	    m_bitrateBox->setAlignment(Qt::AlignRight);
	    m_bitrateBox->setReadOnly(true);
	    trackRowLayout->addWidget(addHidden(m_bitrateBox));
423 424
        }

425 426
        m_commentBox = new KEdit(this, "commentBox");
	m_commentBox->setTextFormat(Qt::PlainText);
427 428 429
	addItem(i18n("&Comment:"), m_commentBox, rightColumnLayout, "edit");
    	fileNameLabel->setMinimumHeight(m_trackSpin->height());

430 431
    }

Scott Wheeler's avatar
Scott Wheeler committed
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
    connect(m_artistNameBox, SIGNAL(textChanged(const QString&)),
	    this, SLOT(slotDataChanged()));

    connect(m_trackNameBox, SIGNAL(textChanged(const QString&)),
	    this, SLOT(slotDataChanged()));

    connect(m_albumNameBox, SIGNAL(textChanged(const QString&)),
	    this, SLOT(slotDataChanged()));

    connect(m_genreBox, SIGNAL(activated(int)),
	    this, SLOT(slotDataChanged()));

    connect(m_genreBox, SIGNAL(textChanged(const QString&)),
	    this, SLOT(slotDataChanged()));

    connect(m_fileNameBox, SIGNAL(textChanged(const QString&)),
	    this, SLOT(slotDataChanged()));

    connect(m_yearSpin, SIGNAL(valueChanged(int)),
	    this, SLOT(slotDataChanged()));

    connect(m_trackSpin, SIGNAL(valueChanged(int)),
	    this, SLOT(slotDataChanged()));

    connect(m_commentBox, SIGNAL(textChanged()),
	    this, SLOT(slotDataChanged()));
458 459 460 461
}

void TagEditor::save(const PlaylistItemList &list)
{
462
    if(!list.isEmpty() && m_dataChanged) {
463 464 465 466 467 468 469
	
	KApplication::setOverrideCursor(Qt::waitCursor);

	// To keep track of the files that don't cooperate...

	QStringList errorFiles;
	
470
	for(PlaylistItemList::ConstIterator it = list.begin(); it != list.end(); ++it) {
471 472
	    PlaylistItem *item = *it;
	    
473 474 475
	    QFileInfo newFile(item->file().fileInfo().dirPath() + QDir::separator() +
			      m_fileNameBox->text());
	    QFileInfo directory(item->file().fileInfo().dirPath());
476 477 478 479 480 481
	    
	    // If (the new file is writable or the new file doesn't exist and
	    // it's directory is writable) and the old file is writable...  
	    // If not we'll append it to errorFiles to tell the user which
	    // files we couldn't write to.
	    
Scott Wheeler's avatar
Scott Wheeler committed
482 483
	    if(item &&
	       item->file().tag() &&
484
	       (newFile.isWritable() || (!newFile.exists() && directory.isWritable())) &&
485
	       item->file().fileInfo().isWritable())
Scott Wheeler's avatar
Scott Wheeler committed
486
	    {
487 488 489 490
		
		// If the file name in the box doesn't match the current file
		// name...
		
491 492 493
		if(list.count() == 1 &&
		   item->file().fileInfo().fileName() != newFile.fileName())
		{
494
		    
495 496
		    // Rename the file if it doesn't exist or the user says
		    // that it's ok.
497
		    
498
		    if(!newFile.exists() ||
Scott Wheeler's avatar
Scott Wheeler committed
499 500 501 502
		       KMessageBox::warningYesNo(
			   this, 
			   i18n("This file already exists.\nDo you want to replace it?"),
			   i18n("File Exists")) == KMessageBox::Yes)
503 504
		    {
			QDir currentDir;
505 506
			currentDir.rename(item->file().absFilePath(), newFile.filePath());
			item->file().setFile(newFile.filePath());
507 508
		    }
		}
509 510 511 512 513 514 515
		
		// 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.
		
516
		if(m_enableBoxes[m_artistNameBox]->isOn())
517
		    item->file().tag()->setArtist(m_artistNameBox->currentText());
518
		if(m_enableBoxes[m_trackNameBox]->isOn())
519
		    item->file().tag()->setTitle(m_trackNameBox->text());
520
		if(m_enableBoxes[m_albumNameBox]->isOn())
521
		    item->file().tag()->setAlbum(m_albumNameBox->currentText());
522
		if(m_enableBoxes[m_trackSpin]->isOn())
523
		    item->file().tag()->setTrack(m_trackSpin->value());
524
		if(m_enableBoxes[m_yearSpin]->isOn())
525
		    item->file().tag()->setYear(m_yearSpin->value());
526
		if(m_enableBoxes[m_commentBox]->isOn())
527
		    item->file().tag()->setComment(m_commentBox->text());
528
		
529
		if(m_enableBoxes[m_genreBox]->isOn())
530
		    item->file().tag()->setGenre(m_genreBox->currentText());
531
		
532
		item->file().tag()->save();
533
		
534
		item->refresh();
535
	    }
Scott Wheeler's avatar
Scott Wheeler committed
536
	    else if(item)
537
		errorFiles.append(item->file().absFilePath());
538 539

	    kapp->processEvents();
540 541 542 543 544
	}
	
	if(!errorFiles.isEmpty())
	    KMessageBox::detailedSorry(this,
				       i18n("Could not save to specified file(s)."), 
545
				       i18n("Could Not Write to:\n") + errorFiles.join("\n"));
546
	m_dataChanged = false;
547 548

	KApplication::restoreOverrideCursor();
549 550 551 552 553
    }
}

void TagEditor::saveChangesPrompt()
{
554 555 556 557 558 559
    if(!isVisible() || !m_dataChanged || m_items.isEmpty())
	return;

    QStringList files;

    for(PlaylistItemList::Iterator it = m_items.begin(); it != m_items.end(); it++)
560
	files.append((*it)->file().absFilePath());
561 562 563 564 565 566 567 568 569 570

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

574
void TagEditor::addItem(const QString &text, QWidget *item, QBoxLayout *layout, const QString &iconName)
575 576 577 578
{
    if(!item || !layout)
	return;

579
    QLabel *label = new QLabel(item, text, this);
580 581 582 583 584
    QLabel *iconLabel = new QLabel(item, 0, this);
    
    if(!iconName.isNull())
	iconLabel->setPixmap(SmallIcon(iconName));
    
585 586 587 588 589 590
    QCheckBox *enableBox = new QCheckBox(i18n("Enable"), this);
    enableBox->setChecked(true);

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

    if(layout->direction() == QBoxLayout::LeftToRight) {
591
    	layout->addWidget(iconLabel);
592 593 594 595 596 597
	layout->addWidget(label);
	layout->addWidget(item);
	layout->addWidget(enableBox);
    }
    else {
	QHBoxLayout *l = new QHBoxLayout(layout);
598 599
	
	l->addWidget(iconLabel);
600 601 602 603 604 605 606 607 608 609 610 611 612 613
	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, SIGNAL(toggled(bool)), item, SLOT(setEnabled(bool)));
614
    m_enableBoxes.insert(item, enableBox);
615 616
}

617 618
void TagEditor::showEvent(QShowEvent *e)
{
619
    slotRefresh();
620 621 622
    QWidget::showEvent(e);
}

623 624 625 626 627 628 629 630
bool TagEditor::eventFilter(QObject *watched, QEvent *e)
{
    if(watched->inherits("QSpinBox") && e->type() == QEvent::KeyRelease)
	slotDataChanged();

    return false;
}

631 632 633 634
////////////////////////////////////////////////////////////////////////////////
// private slots
////////////////////////////////////////////////////////////////////////////////

635
void TagEditor::slotDataChanged(bool c)
636
{
637
    m_dataChanged = c;
638 639
}

640 641 642 643 644 645 646
void TagEditor::slotItemRemoved(PlaylistItem *item)
{
    m_items.remove(item);
    if(m_items.isEmpty())
	slotRefresh();
}

647
#include "tageditor.moc"