Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

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

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

16 17 18 19
#include "tageditor.h"
#include "collectionlist.h"
#include "playlistitem.h"
#include "tag.h"
20
#include "actioncollection.h"
21

22 23 24 25
#include <kcombobox.h>
#include <klineedit.h>
#include <knuminput.h>
#include <keditcl.h>
26
#include <kmessagebox.h>
27
#include <kconfig.h>
28 29
#include <klocale.h>
#include <kdebug.h>
30
#include <kiconloader.h>
31
#include <kactionclasses.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
#include <qtooltip.h>
39
#include <qeventloop.h>
40

41 42
#include <id3v1genres.h>

43 44
using namespace ActionCollection;

Scott Wheeler's avatar
Scott Wheeler committed
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
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;
    }
};

64 65 66 67
class FileBoxToolTip : public QToolTip
{
public:
    FileBoxToolTip(TagEditor *editor, QWidget *widget) :
68
	QToolTip(widget), m_editor(editor) {}
69 70 71
protected:
    virtual void maybeTip(const QPoint &)
    {
72
	tip(parentWidget()->rect(), m_editor->items().first()->file().absFilePath());
73 74 75 76 77
    }
private:
    TagEditor *m_editor;
};

78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
class FixedHLayout : public QHBoxLayout
{
public:
    FixedHLayout(QWidget *parent, int margin = 0, int spacing = -1, const char *name = 0) :
	QHBoxLayout(parent, margin, spacing, name),
	m_width(-1) {}
    FixedHLayout(QLayout *parentLayout, int spacing = -1, const char *name = 0) :
	QHBoxLayout(parentLayout, spacing, name),
	m_width(-1) {}
    void setWidth(int w = -1)
    {
	m_width = w == -1 ? QHBoxLayout::minimumSize().width() : w;
    }
    virtual QSize minimumSize() const
    {
	QSize s = QHBoxLayout::minimumSize();
	s.setWidth(m_width);
	return s;
    }
private:
    int m_width;
};

101 102 103 104
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

105 106 107
TagEditor::TagEditor(QWidget *parent, const char *name) :
    QWidget(parent, name),
    m_currentPlaylist(0)
108
{
109
    setupActions();
110 111
    setupLayout();
    readConfig();
112
    m_dataChanged = false;
113 114 115 116 117 118 119 120 121 122 123
}

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

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

124
void TagEditor::slotSetItems(const PlaylistItemList &list)
125 126
{
    saveChangesPrompt();
127 128 129 130 131 132 133 134 135 136 137

    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 *)));
138
	connect(m_currentPlaylist, SIGNAL(destroyed()), this, SLOT(slotPlaylistRemoved()));
139 140
    }

141
    m_items = list;
142

143
    if(isVisible())
144
	slotRefresh();
145 146
}

147
void TagEditor::slotRefresh()
148
{
149
    // This method takes the list of currently selected m_items and tries to 
150 151
    // 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
152
    // check after that to see if there are other m_items and adjust accordingly.
153

154
    if(m_items.isEmpty() || !m_items.first()->file().tag()) {
155
	slotClear();
156
	setEnabled(false);
157 158 159
	return;
    }
    
160 161
    setEnabled(true);

162
    PlaylistItem *item = m_items.first();
163

164
    Tag *tag = item->file().tag();
165
	
166
    m_artistNameBox->setEditText(tag->artist());
167
    m_trackNameBox->setText(tag->title());
168
    m_albumNameBox->setEditText(tag->album());
169

170
    m_fileNameBox->setText(item->file().fileInfo().fileName());
171
    new FileBoxToolTip(this, m_fileNameBox);
172
    m_bitrateBox->setText(QString::number(tag->bitrate()));
173 174
    m_lengthBox->setText(tag->lengthString());

175 176
    if(m_genreList.findIndex(tag->genre()) >= 0)
	m_genreBox->setCurrentItem(m_genreList.findIndex(tag->genre()) + 1);
177 178
    else {
	m_genreBox->setCurrentItem(0);
179
	m_genreBox->setEditText(tag->genre());
180
    }
181

182
    m_trackSpin->setValue(tag->track());
183 184 185 186 187 188 189 190 191 192 193 194
    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()) {
195 196 197 198

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

	BoxMap::Iterator boxIt = m_enableBoxes.begin();
	for(; boxIt != m_enableBoxes.end(); boxIt++) {
202 203
	    (*boxIt)->setChecked(true);
	    (*boxIt)->show();
204 205
	}
	
206 207 208
	// 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.
209
	
210
	// Also, if there are more than 50 m_items, don't scan all of them.
211
	
212 213 214 215 216 217 218 219
	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);
220 221
	}
	else {
222
	    for(; it != m_items.end(); ++it) {
223
		tag = (*it)->file().tag();
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268

		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);
		    }
269
		}
270 271
	    }
	}
272
    }
273 274
    else {
	// Clean up in the case that we are only handling one item.
275 276 277 278

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

	BoxMap::iterator boxIt = m_enableBoxes.begin();
	for(; boxIt != m_enableBoxes.end(); boxIt++) {
282 283 284 285 286
	    (*boxIt)->setChecked(true);
	    (*boxIt)->hide();
	}
    }
    m_dataChanged = false;
287 288
}

289
void TagEditor::slotClear()
290
{
291 292 293 294 295 296 297 298 299 300
    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();    
301 302
}

303
void TagEditor::slotUpdateCollection()
304
{
305 306 307 308 309
    CollectionList *list = CollectionList::instance();

    if(!list)
	return;
    
310 311 312 313 314 315 316 317 318
    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);
319 320 321 322 323 324
}

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

325
void TagEditor::readConfig()
326
{
327 328
    KConfig *config = KGlobal::config();
    { // combo box completion modes
329 330
	KConfigGroupSaver saver(config, "TagEditor");
	if(m_artistNameBox && m_albumNameBox) {
331 332 333
	    readCompletionMode(config, m_artistNameBox, "ArtistNameBoxMode");
	    readCompletionMode(config, m_albumNameBox, "AlbumNameBoxMode");
	    readCompletionMode(config, m_genreBox, "GenreBoxMode");
334
        }
335 336 337 338

	bool show = config->readBoolEntry("Show", false);
        action<KToggleAction>("showEditor")->setChecked(show);
	setShown(show);
339
    }
340

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

343 344 345
    for(TagLib::StringList::ConstIterator it = genres.begin(); it != genres.end(); ++it)
	m_genreList.append(TStringToQString((*it)));
    m_genreList.sort();
346

347 348 349
    m_genreBox->clear();
    m_genreBox->insertItem(QString::null);
    m_genreBox->insertStringList(m_genreList);
350 351 352 353 354 355 356 357 358
    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);
359
}
360

361 362 363 364 365
void TagEditor::saveConfig()
{
    KConfig *config = KGlobal::config();
    { // combo box completion modes
        KConfigGroupSaver saver(config, "TagEditor");
366 367 368
        if(m_artistNameBox && m_albumNameBox) {
	    config->writeEntry("ArtistNameBoxMode", m_artistNameBox->completionMode());
	    config->writeEntry("AlbumNameBoxMode", m_albumNameBox->completionMode());
369
	    config->writeEntry("GenreBoxMode", m_genreBox->completionMode());
370
        }
371
	config->writeEntry("Show", action<KToggleAction>("showEditor")->isChecked());
372
    }
373

374
}
375

376 377 378 379 380 381 382 383 384
void TagEditor::setupActions()
{
    KToggleAction *show = new KToggleAction(i18n("Show &Tag Editor"), "edit", 0, actions(), "showEditor");
    show->setCheckedState(i18n("Hide &Tag Editor"));
    connect(show, SIGNAL(toggled(bool)), this, SLOT(setShown(bool)));

    new KAction(i18n("&Save"), "filesave", "CTRL+t", this, SLOT(slotSave()), actions(), "saveItem");
}

385 386
void TagEditor::setupLayout()
{
387
    static const int horizontalSpacing = 12;
388
    static const int verticalSpacing = 2;
389

390
    QHBoxLayout *layout = new QHBoxLayout(this, 6, horizontalSpacing);
391 392 393 394 395 396 397 398 399 400 401 402 403 404

    //////////////////////////////////////////////////////////////////////////////
    // 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
405 406
    	
	m_artistNameBox = new KComboBox(true, this, "artistNameBox");
407
	m_artistNameBox->setCompletionMode(KGlobalSettings::CompletionAuto);
408
	addItem(i18n("&Artist name:"), m_artistNameBox, leftColumnLayout, "personal");
409

410
        m_trackNameBox = new KLineEdit(this, "trackNameBox");
411
	addItem(i18n("&Track name:"), m_trackNameBox, leftColumnLayout, "player_play");
412

413
	m_albumNameBox = new KComboBox(true, this, "albumNameBox");
414
	m_albumNameBox->setCompletionMode(KGlobalSettings::CompletionAuto);
415
	addItem(i18n("Album &name:"), m_albumNameBox, leftColumnLayout, "cdrom_unmount");
416

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

        // this fills the space at the bottem of the left column
Scott Wheeler's avatar
Scott Wheeler committed
421 422
        leftColumnLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum,
						  QSizePolicy::Expanding));
423 424 425 426 427
    }
    //////////////////////////////////////////////////////////////////////////////
    // put stuff in the right column
    //////////////////////////////////////////////////////////////////////////////
    { // just for organization
428 429
	
	QHBoxLayout *fileNameLayout = new QHBoxLayout(rightColumnLayout,
430
						      horizontalSpacing);
431

432
	m_fileNameBox = new KLineEdit(this, "fileNameBox");
Scott Wheeler's avatar
Scott Wheeler committed
433 434
	m_fileNameBox->setValidator(new FileNameValidator(m_fileNameBox));	

435 436
	QLabel *fileNameIcon = new QLabel(this);
	fileNameIcon->setPixmap(SmallIcon("sound"));
Scott Wheeler's avatar
Scott Wheeler committed
437 438
	QWidget *fileNameLabel = addHidden(new QLabel(m_fileNameBox, i18n("&File name:"), this));

439 440 441 442 443 444
	fileNameLayout->addWidget(addHidden(fileNameIcon));
	fileNameLayout->addWidget(fileNameLabel);
	fileNameLayout->setStretchFactor(fileNameIcon, 0);
	fileNameLayout->setStretchFactor(fileNameLabel, 0);
	fileNameLayout->insertStretch(-1, 1);
	rightColumnLayout->addWidget(addHidden(m_fileNameBox));
445 446

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

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

454
	    trackRowLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding,
Scott Wheeler's avatar
Scott Wheeler committed
455
						    QSizePolicy::Minimum));
456

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

461
	    trackRowLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding,
Scott Wheeler's avatar
Scott Wheeler committed
462
						    QSizePolicy::Minimum));
463

464 465 466 467 468 469 470
	    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));
471

472
	    trackRowLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding,
Scott Wheeler's avatar
Scott Wheeler committed
473
						    QSizePolicy::Minimum));
474

475 476 477 478 479 480 481
	    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));
482 483

	    trackRowLayout->setWidth();
484 485
        }

486 487
        m_commentBox = new KEdit(this, "commentBox");
	m_commentBox->setTextFormat(Qt::PlainText);
488 489 490
	addItem(i18n("&Comment:"), m_commentBox, rightColumnLayout, "edit");
    	fileNameLabel->setMinimumHeight(m_trackSpin->height());

491 492
    }

Scott Wheeler's avatar
Scott Wheeler committed
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
    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()));
519 520 521 522
}

void TagEditor::save(const PlaylistItemList &list)
{
523
    if(!list.isEmpty() && m_dataChanged) {
524 525 526 527 528 529 530
	
	KApplication::setOverrideCursor(Qt::waitCursor);

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

	QStringList errorFiles;
	
531
	for(PlaylistItemList::ConstIterator it = list.begin(); it != list.end(); ++it) {
532 533
	    PlaylistItem *item = *it;
	    
534 535 536
	    QFileInfo newFile(item->file().fileInfo().dirPath() + QDir::separator() +
			      m_fileNameBox->text());
	    QFileInfo directory(item->file().fileInfo().dirPath());
537 538 539 540 541 542
	    
	    // 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
543 544
	    if(item &&
	       item->file().tag() &&
545
	       (newFile.isWritable() || (!newFile.exists() && directory.isWritable())) &&
546
	       item->file().fileInfo().isWritable())
Scott Wheeler's avatar
Scott Wheeler committed
547
	    {
548 549 550 551
		
		// If the file name in the box doesn't match the current file
		// name...
		
552 553 554
		if(list.count() == 1 &&
		   item->file().fileInfo().fileName() != newFile.fileName())
		{
555
		    
556 557
		    // Rename the file if it doesn't exist or the user says
		    // that it's ok.
558
		    
559
		    if(!newFile.exists() ||
Scott Wheeler's avatar
Scott Wheeler committed
560 561 562 563
		       KMessageBox::warningYesNo(
			   this, 
			   i18n("This file already exists.\nDo you want to replace it?"),
			   i18n("File Exists")) == KMessageBox::Yes)
564 565
		    {
			QDir currentDir;
566 567
			currentDir.rename(item->file().absFilePath(), newFile.filePath());
			item->file().setFile(newFile.filePath());
568 569
		    }
		}
570 571 572 573 574 575 576
		
		// 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.
		
577
		if(m_enableBoxes[m_artistNameBox]->isOn())
578
		    item->file().tag()->setArtist(m_artistNameBox->currentText());
579
		if(m_enableBoxes[m_trackNameBox]->isOn())
580
		    item->file().tag()->setTitle(m_trackNameBox->text());
581
		if(m_enableBoxes[m_albumNameBox]->isOn())
582
		    item->file().tag()->setAlbum(m_albumNameBox->currentText());
583 584 585
		if(m_enableBoxes[m_trackSpin]->isOn()) {
		    if(m_trackSpin->text().isEmpty())
			m_trackSpin->setValue(0);
586
		    item->file().tag()->setTrack(m_trackSpin->value());
587 588 589 590
		}
		if(m_enableBoxes[m_yearSpin]->isOn()) {
		    if(m_yearSpin->text().isEmpty())
			m_yearSpin->setValue(0);
591
		    item->file().tag()->setYear(m_yearSpin->value());
592
		}
593
		if(m_enableBoxes[m_commentBox]->isOn())
594
		    item->file().tag()->setComment(m_commentBox->text());
595
		
596
		if(m_enableBoxes[m_genreBox]->isOn())
597
		    item->file().tag()->setGenre(m_genreBox->currentText());
598
		
599
		item->file().tag()->save();
600
		
601
		item->refresh();
602
	    }
Scott Wheeler's avatar
Scott Wheeler committed
603
	    else if(item)
604
		errorFiles.append(item->file().absFilePath());
605

606
	    kapp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
607 608 609 610 611
	}
	
	if(!errorFiles.isEmpty())
	    KMessageBox::detailedSorry(this,
				       i18n("Could not save to specified file(s)."), 
612
				       i18n("Could Not Write to:\n") + errorFiles.join("\n"));
613
	m_dataChanged = false;
614 615

	KApplication::restoreOverrideCursor();
616 617 618 619 620
    }
}

void TagEditor::saveChangesPrompt()
{
621 622 623 624 625 626
    if(!isVisible() || !m_dataChanged || m_items.isEmpty())
	return;

    QStringList files;

    for(PlaylistItemList::Iterator it = m_items.begin(); it != m_items.end(); it++)
627
	files.append((*it)->file().absFilePath());
628 629 630 631 632 633 634 635 636 637

    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);
638 639 640
    }
}

641
void TagEditor::addItem(const QString &text, QWidget *item, QBoxLayout *layout, const QString &iconName)
642 643 644 645
{
    if(!item || !layout)
	return;

646
    QLabel *label = new QLabel(item, text, this);
647 648 649 650 651
    QLabel *iconLabel = new QLabel(item, 0, this);
    
    if(!iconName.isNull())
	iconLabel->setPixmap(SmallIcon(iconName));
    
652 653 654 655 656 657
    QCheckBox *enableBox = new QCheckBox(i18n("Enable"), this);
    enableBox->setChecked(true);

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

    if(layout->direction() == QBoxLayout::LeftToRight) {
658
    	layout->addWidget(iconLabel);
659 660 661 662 663 664
	layout->addWidget(label);
	layout->addWidget(item);
	layout->addWidget(enableBox);
    }
    else {
	QHBoxLayout *l = new QHBoxLayout(layout);
665 666
	
	l->addWidget(iconLabel);
667 668 669 670 671 672 673 674 675 676 677 678 679 680
	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)));
681
    m_enableBoxes.insert(item, enableBox);
682 683
}

684 685
void TagEditor::showEvent(QShowEvent *e)
{
686
    slotRefresh();
687 688 689
    QWidget::showEvent(e);
}

690 691 692 693 694 695 696 697
bool TagEditor::eventFilter(QObject *watched, QEvent *e)
{
    if(watched->inherits("QSpinBox") && e->type() == QEvent::KeyRelease)
	slotDataChanged();

    return false;
}

698 699 700 701
////////////////////////////////////////////////////////////////////////////////
// private slots
////////////////////////////////////////////////////////////////////////////////

702
void TagEditor::slotDataChanged(bool c)
703
{
704
    m_dataChanged = c;
705 706
}

707 708 709 710 711 712 713
void TagEditor::slotItemRemoved(PlaylistItem *item)
{
    m_items.remove(item);
    if(m_items.isEmpty())
	slotRefresh();
}

714
#include "tageditor.moc"