tageditor.cpp 20.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/**
 * Copyright (C) 2002-2004 Scott Wheeler <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.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */
16

17 18 19 20
#include "tageditor.h"
#include "collectionlist.h"
#include "playlistitem.h"
#include "tag.h"
21
#include "actioncollection.h"
22
#include "tagtransactionmanager.h"
23
#include "juk_debug.h"
24

Michael Pyne's avatar
Michael Pyne committed
25
#include <QAction>
26 27
#include <kactioncollection.h>
#include <kconfiggroup.h>
28 29 30
#include <kcombobox.h>
#include <klineedit.h>
#include <knuminput.h>
31
#include <ktextedit.h>
32
#include <kapplication.h>
33
#include <kmessagebox.h>
34
#include <kconfig.h>
35
#include <klocale.h>
36
#include <kiconloader.h>
Michael Pyne's avatar
Michael Pyne committed
37
#include <QIcon>
38
#include <ktoggleaction.h>
39
#include <kshortcut.h>
40
#include <kglobal.h>
41

42
#include <QLabel>
43
#include <QApplication>
44 45
#include <QCheckBox>
#include <QDir>
46 47
#include <QValidator>
#include <QEventLoop>
Laurent Montel's avatar
Laurent Montel committed
48 49 50
#include <QKeyEvent>
#include <QHBoxLayout>
#include <QVBoxLayout>
51
#include <QSizePolicy>
52

53 54
#include <id3v1genres.h>

55 56
#undef KeyRelease

57 58 59 60
class FileNameValidator : public QValidator
{
public:
    FileNameValidator(QObject *parent, const char *name = 0) :
61
        QValidator(parent)
Tim Beaulen's avatar
Tim Beaulen committed
62
    {
63
        setObjectName( QLatin1String( name ) );
Tim Beaulen's avatar
Tim Beaulen committed
64
    }
65 66 67

    virtual void fixup(QString &s) const
    {
68
        s.remove('/');
69 70 71 72
    }

    virtual State validate(QString &s, int &) const
    {
Stephan Kulow's avatar
Stephan Kulow committed
73
        if(s.contains('/'))
74 75
           return Invalid;
        return Acceptable;
76 77 78
    }
};

79 80 81 82
class FixedHLayout : public QHBoxLayout
{
public:
    FixedHLayout(QWidget *parent, int margin = 0, int spacing = -1, const char *name = 0) :
Stephan Kulow's avatar
Stephan Kulow committed
83 84 85
        QHBoxLayout(parent),
        m_width(-1)
    {
Scott Wheeler's avatar
Scott Wheeler committed
86 87
        setMargin(margin);
        setSpacing(spacing);
Arnold Dumas's avatar
Arnold Dumas committed
88
        setObjectName(QLatin1String(name));
Stephan Kulow's avatar
Stephan Kulow committed
89
    }
90
    FixedHLayout(QLayout *parentLayout, int spacing = -1, const char *name = 0) :
Stephan Kulow's avatar
Stephan Kulow committed
91 92 93
        QHBoxLayout(),
        m_width(-1)
    {
Scott Wheeler's avatar
Scott Wheeler committed
94 95
        parentLayout->addItem(this);
        setSpacing(spacing);
Arnold Dumas's avatar
Arnold Dumas committed
96
        setObjectName(QLatin1String(name));
Stephan Kulow's avatar
Stephan Kulow committed
97
    }
98 99
    void setWidth(int w = -1)
    {
100
        m_width = w == -1 ? QHBoxLayout::minimumSize().width() : w;
101 102 103
    }
    virtual QSize minimumSize() const
    {
104 105 106
        QSize s = QHBoxLayout::minimumSize();
        s.setWidth(m_width);
        return s;
107 108 109 110 111
    }
private:
    int m_width;
};

112 113 114 115
class CollectionObserver : public PlaylistObserver
{
public:
    CollectionObserver(TagEditor *parent) :
116 117
        PlaylistObserver(CollectionList::instance()),
        m_parent(parent)
118 119 120 121 122
    {
    }

    virtual void updateData()
    {
123 124
        if(m_parent && m_parent->m_currentPlaylist && m_parent->isVisible())
            m_parent->slotSetItems(m_parent->m_currentPlaylist->selectedItems());
125 126 127 128 129 130 131 132
    }

    virtual void updateCurrent() {}

private:
    TagEditor *m_parent;
};

133 134 135 136
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

Dirk Mueller's avatar
Dirk Mueller committed
137 138
TagEditor::TagEditor(QWidget *parent) :
    QWidget(parent),
139
    m_currentPlaylist(0),
140 141
    m_observer(0),
    m_performingSave(false)
142
{
143
    setupActions();
144 145
    setupLayout();
    readConfig();
146
    m_dataChanged = false;
147
    m_collectionChanged = false;
148 149

    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
150 151 152 153
}

TagEditor::~TagEditor()
{
154
    delete m_observer;
155 156 157
    saveConfig();
}

158 159 160 161 162
void TagEditor::setupObservers()
{
    m_observer = new CollectionObserver(this);
}

163 164 165 166
////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////

167
void TagEditor::slotSetItems(const PlaylistItemList &list)
168
{
169
    if(m_performingSave)
170
        return;
171

172 173 174 175 176
    // Store the playlist that we're setting because saveChangesPrompt
    // can delete the PlaylistItems in list.

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

    bool hadPlaylist = m_currentPlaylist != 0;

181
    saveChangesPrompt();
182 183

    if(m_currentPlaylist) {
Laurent Montel's avatar
Laurent Montel committed
184 185
        disconnect(m_currentPlaylist, SIGNAL(signalAboutToRemove(PlaylistItem*)),
                   this, SLOT(slotItemRemoved(PlaylistItem*)));
186 187
    }

188
    if((hadPlaylist && !m_currentPlaylist) || !itemPlaylist) {
189 190
        m_currentPlaylist = 0;
        m_items.clear();
191 192
    }
    else {
193
        m_currentPlaylist = itemPlaylist;
194

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

197
        m_items = itemPlaylist->selectedItems();
198
    }
199 200

    if(m_currentPlaylist) {
Laurent Montel's avatar
Laurent Montel committed
201 202
        connect(m_currentPlaylist, SIGNAL(signalAboutToRemove(PlaylistItem*)),
                this, SLOT(slotItemRemoved(PlaylistItem*)));
203
        connect(m_currentPlaylist, SIGNAL(destroyed()), this, SLOT(slotPlaylistRemoved()));
204 205
    }

206
    if(isVisible())
207
        slotRefresh();
208
    else
209
        m_collectionChanged = true;
210 211
}

212
void TagEditor::slotRefresh()
213
{
214
    // This method takes the list of currently selected m_items and tries to
215 216
    // 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
217
    // check after that to see if there are other m_items and adjust accordingly.
218

219
    if(m_items.isEmpty() || !m_items.first()->file().tag()) {
220 221 222
        slotClear();
        setEnabled(false);
        return;
223
    }
224

225 226
    setEnabled(true);

227
    PlaylistItem *item = m_items.first();
228 229

    Q_ASSERT(item);
230

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

233 234
    QFileInfo fi(item->file().absFilePath());
    if(!fi.isWritable() && m_items.count() == 1)
235
        setEnabled(false);
236

237 238 239
    artistNameBox->setEditText(tag->artist());
    trackNameBox->setText(tag->title());
    albumNameBox->setEditText(tag->album());
240

241 242
    fileNameBox->setText(item->file().fileInfo().fileName());
    fileNameBox->setToolTip(item->file().absFilePath());
243

244 245
    bitrateBox->setText(QString::number(tag->bitrate()));
    lengthBox->setText(tag->lengthString());
246

Tim Beaulen's avatar
Tim Beaulen committed
247
    if(m_genreList.indexOf(tag->genre()) >= 0)
248
        genreBox->setCurrentIndex(m_genreList.indexOf(tag->genre()) + 1);
249
    else {
250 251
        genreBox->setCurrentIndex(0);
        genreBox->setEditText(tag->genre());
252
    }
253

254 255
    trackSpin->setValue(tag->track());
    yearSpin->setValue(tag->year());
256

257
    commentBox->setPlainText(tag->comment());
258

259
    // Start at the second item, since we've already processed the first.
260

261 262 263 264
    PlaylistItemList::Iterator it = m_items.begin();
    ++it;

    // If there is more than one item in the m_items that we're dealing with...
265

266

267 268 269 270 271 272 273 274 275 276 277 278
    QList<QWidget *> disabledForMulti;

    disabledForMulti << fileNameLabel << fileNameBox << lengthLabel << lengthBox
                     << bitrateLabel << bitrateBox;

    foreach(QWidget *w, disabledForMulti) {
        w->setDisabled(m_items.size() > 1);
        if(m_items.size() > 1 && !w->inherits("QLabel"))
            QMetaObject::invokeMethod(w, "clear");
    }

    if(it != m_items.end()) {
279

280 281 282
        foreach(QCheckBox *box, m_enableBoxes) {
            box->setChecked(true);
            box->show();
283 284 285 286 287 288 289 290 291
        }

        // 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) {
292 293 294 295 296 297 298
            m_enableBoxes[artistNameBox]->setChecked(false);
            m_enableBoxes[trackNameBox]->setChecked(false);
            m_enableBoxes[albumNameBox]->setChecked(false);
            m_enableBoxes[genreBox]->setChecked(false);
            m_enableBoxes[trackSpin]->setChecked(false);
            m_enableBoxes[yearSpin]->setChecked(false);
            m_enableBoxes[commentBox]->setChecked(false);
299 300 301 302 303 304 305
        }
        else {
            for(; it != m_items.end(); ++it) {
                tag = (*it)->file().tag();

                if(tag) {

306 307
                    if(artistNameBox->currentText() != tag->artist() &&
                       m_enableBoxes.contains(artistNameBox))
308
                    {
309 310
                        artistNameBox->lineEdit()->clear();
                        m_enableBoxes[artistNameBox]->setChecked(false);
311
                    }
312 313
                    if(trackNameBox->text() != tag->title() &&
                       m_enableBoxes.contains(trackNameBox))
314
                    {
315 316
                        trackNameBox->clear();
                        m_enableBoxes[trackNameBox]->setChecked(false);
317
                    }
318 319
                    if(albumNameBox->currentText() != tag->album() &&
                       m_enableBoxes.contains(albumNameBox))
320
                    {
321 322
                        albumNameBox->lineEdit()->clear();
                        m_enableBoxes[albumNameBox]->setChecked(false);
323
                    }
324 325
                    if(genreBox->currentText() != tag->genre() &&
                       m_enableBoxes.contains(genreBox))
326
                    {
327 328
                        genreBox->lineEdit()->clear();
                        m_enableBoxes[genreBox]->setChecked(false);
329
                    }
330 331
                    if(trackSpin->value() != tag->track() &&
                       m_enableBoxes.contains(trackSpin))
332
                    {
333 334
                        trackSpin->setValue(0);
                        m_enableBoxes[trackSpin]->setChecked(false);
335
                    }
336 337
                    if(yearSpin->value() != tag->year() &&
                       m_enableBoxes.contains(yearSpin))
338
                    {
339 340
                        yearSpin->setValue(0);
                        m_enableBoxes[yearSpin]->setChecked(false);
341
                    }
342 343
                    if(commentBox->toPlainText() != tag->comment() &&
                       m_enableBoxes.contains(commentBox))
344
                    {
345 346
                        commentBox->clear();
                        m_enableBoxes[commentBox]->setChecked(false);
347 348 349 350
                    }
                }
            }
        }
351
    }
352
    else {
353 354 355
        foreach(QCheckBox *box, m_enableBoxes) {
            box->setChecked(true);
            box->hide();
356
        }
357 358
    }
    m_dataChanged = false;
359 360
}

361
void TagEditor::slotClear()
362
{
363 364 365 366 367 368 369 370 371 372 373
    artistNameBox->lineEdit()->clear();
    trackNameBox->clear();
    albumNameBox->lineEdit()->clear();
    genreBox->setCurrentIndex(0);
    fileNameBox->clear();
    fileNameBox->setToolTip(QString());
    trackSpin->setValue(0);
    yearSpin->setValue(0);
    lengthBox->clear();
    bitrateBox->clear();
    commentBox->clear();
374 375
}

376
void TagEditor::slotUpdateCollection()
377
{
378
    if(isVisible())
379
        updateCollection();
380
    else
381
        m_collectionChanged = true;
382 383 384 385 386
}

void TagEditor::updateCollection()
{
    m_collectionChanged = false;
387

388 389 390
    CollectionList *list = CollectionList::instance();

    if(!list)
391 392
        return;

393
    QStringList artistList = list->uniqueSet(CollectionList::Artists);
394
    artistList.sort();
395 396 397
    artistNameBox->clear();
    artistNameBox->addItems(artistList);
    artistNameBox->completionObject()->setItems(artistList);
398 399

    QStringList albumList = list->uniqueSet(CollectionList::Albums);
400
    albumList.sort();
401 402 403
    albumNameBox->clear();
    albumNameBox->addItems(albumList);
    albumNameBox->completionObject()->setItems(albumList);
404

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

407
    StringHash genreHash;
408 409

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

411 412
    foreach(const QString &genre, m_genreList)
        genreHash.insert(genre);
413 414 415

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

Laurent Montel's avatar
Laurent Montel committed
416
    for(TagLib::StringList::Iterator it = genres.begin(); it != genres.end(); ++it)
417
        genreHash.insert(TStringToQString((*it)));
418

419
    m_genreList = genreHash.values();
420 421
    m_genreList.sort();

422 423 424 425
    genreBox->clear();
    genreBox->addItem(QString());
    genreBox->addItems(m_genreList);
    genreBox->completionObject()->setItems(m_genreList);
426 427 428 429

    // We've cleared out the original entries of these list boxes, re-read
    // the current item if one is selected.
    slotRefresh();
430 431 432 433 434 435
}

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

436
void TagEditor::readConfig()
437
{
438
    // combo box completion modes
439

440
    KConfigGroup config(KSharedConfig::openConfig(), "TagEditor");
441 442 443 444
    if(artistNameBox && albumNameBox) {
        readCompletionMode(config, artistNameBox, "ArtistNameBoxMode");
        readCompletionMode(config, albumNameBox, "AlbumNameBoxMode");
        readCompletionMode(config, genreBox, "GenreBoxMode");
445
    }
446

447
    bool show = config.readEntry("Show", false);
448
    ActionCollection::action<KToggleAction>("showEditor")->setChecked(show);
Tim Beaulen's avatar
Tim Beaulen committed
449
    setVisible(show);
450

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

453
    for(TagLib::StringList::ConstIterator it = genres.begin(); it != genres.end(); ++it)
454
        m_genreList.append(TStringToQString((*it)));
455
    m_genreList.sort();
456

457 458 459 460
    genreBox->clear();
    genreBox->addItem(QString());
    genreBox->addItems(m_genreList);
    genreBox->completionObject()->setItems(m_genreList);
461 462
}

Stephan Kulow's avatar
Stephan Kulow committed
463
void TagEditor::readCompletionMode(const KConfigGroup &config, KComboBox *box, const QString &key)
464 465
{
    KGlobalSettings::Completion mode =
Stephan Kulow's avatar
Stephan Kulow committed
466
        KGlobalSettings::Completion(config.readEntry(key, (int)KGlobalSettings::CompletionAuto));
467

468
        // FIXME tag completion
469
    //box->setCompletionMode(mode);
470
}
471

472 473
void TagEditor::saveConfig()
{
474
    // combo box completion modes
475

476
    KConfigGroup config(KSharedConfig::openConfig(), "TagEditor");
477

478 479 480 481
    if(artistNameBox && albumNameBox) {
        config.writeEntry("ArtistNameBoxMode", (int)artistNameBox->completionMode());
        config.writeEntry("AlbumNameBoxMode", (int)albumNameBox->completionMode());
        config.writeEntry("GenreBoxMode", (int)genreBox->completionMode());
482
    }
483
    config.writeEntry("Show", ActionCollection::action<KToggleAction>("showEditor")->isChecked());
484
}
485

486 487
void TagEditor::setupActions()
{
Michael Pyne's avatar
Michael Pyne committed
488
    KToggleAction *show = new KToggleAction(QIcon::fromTheme(QLatin1String("document-properties")),
489
                                            i18n("Show &Tag Editor"), this);
490
    ActionCollection::actions()->addAction("showEditor", show);
491
    connect(show, SIGNAL(toggled(bool)), this, SLOT(setVisible(bool)));
492

Michael Pyne's avatar
Michael Pyne committed
493
    QAction *act = new QAction(QIcon::fromTheme(QLatin1String( "document-save")), i18n("&Save"), this);
494
    ActionCollection::actions()->addAction("saveItem", act);
495 496
    act->setShortcut(Qt::CTRL + Qt::Key_T);
    connect(act, SIGNAL(triggered(bool)), SLOT(slotSave()));
497 498
}

499 500
void TagEditor::setupLayout()
{
501 502 503
    setupUi(this);

    foreach(QWidget *input, findChildren<QWidget *>()) {
504
        if(input->inherits("QLineEdit") || input->inherits("QComboBox"))
Laurent Montel's avatar
Laurent Montel committed
505
            connect(input, SIGNAL(textChanged(QString)), this, SLOT(slotDataChanged()));
506 507 508 509 510
        if(input->inherits("QComboxBox"))
            connect(input, SIGNAL(activated(int)), this, SLOT(slotDataChanged()));
        if(input->inherits("QSpinBox"))
            connect(input, SIGNAL(valueChanged(int)), this, SLOT(slotDataChanged()));
        if(input->inherits("QTextEdit"))
511
            connect(input, SIGNAL(textChanged()), this, SLOT(slotDataChanged()));
512
    }
513 514 515 516 517

    // Do some meta-programming to find the matching enable boxes

    foreach(QCheckBox *enable, findChildren<QCheckBox *>(QRegExp("Enable"))) {
        enable->hide();
518
        QRegExp re('^' + enable->objectName().replace("Enable", "") + "(Box|Spin)$");
519 520 521 522
        QList<QWidget *> targets = findChildren<QWidget *>(re);
        Q_ASSERT(!targets.isEmpty());
        m_enableBoxes[targets.front()] = enable;
    }
523 524 525 526 527 528 529 530 531 532

    // Make sure that the labels are as tall as the enable buttons so that the
    // layout doesn't jump around.

    foreach(QLabel *label, findChildren<QLabel *>()) {
        if(m_enableBoxes.contains(label->buddy()))
            label->setMinimumHeight(m_enableBoxes[label->buddy()]->height());
    }

    tagEditorLayout->setColumnMinimumWidth(1, 200);
533 534 535 536
}

void TagEditor::save(const PlaylistItemList &list)
{
537
    if(!list.isEmpty() && m_dataChanged) {
538

539
        QApplication::setOverrideCursor(Qt::WaitCursor);
540 541 542 543 544 545 546 547 548
        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();

549
        for(PlaylistItemList::ConstIterator it = list.begin(); it != end; /* Deliberately missing */ ) {
550 551 552 553 554

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

555
            qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
556 557 558 559 560 561 562 563 564

            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;

Tim Beaulen's avatar
Tim Beaulen committed
565
            QString fileName = item->file().fileInfo().path() + QDir::separator() +
566
                               fileNameBox->text();
567
            if(list.count() > 1)
Tim Beaulen's avatar
Tim Beaulen committed
568
                fileName = item->file().fileInfo().absoluteFilePath();
569 570 571 572 573 574 575 576 577

            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.

578 579 580 581 582 583 584 585 586 587
            if(m_enableBoxes[artistNameBox]->isChecked())
                tag->setArtist(artistNameBox->currentText());
            if(m_enableBoxes[trackNameBox]->isChecked())
                tag->setTitle(trackNameBox->text());
            if(m_enableBoxes[albumNameBox]->isChecked())
                tag->setAlbum(albumNameBox->currentText());
            if(m_enableBoxes[trackSpin]->isChecked()) {
                if(trackSpin->text().isEmpty())
                    trackSpin->setValue(0);
                tag->setTrack(trackSpin->value());
588
            }
589 590 591 592
            if(m_enableBoxes[yearSpin]->isChecked()) {
                if(yearSpin->text().isEmpty())
                    yearSpin->setValue(0);
                tag->setYear(yearSpin->value());
593
            }
594 595
            if(m_enableBoxes[commentBox]->isChecked())
                tag->setComment(commentBox->toPlainText());
596

597 598
            if(m_enableBoxes[genreBox]->isChecked())
                tag->setGenre(genreBox->currentText());
599 600 601 602 603 604 605

            TagTransactionManager::instance()->changeTagOnItem(item, tag);
        }

        TagTransactionManager::instance()->commit();
        CollectionList::instance()->dataChanged();
        m_performingSave = false;
606
        QApplication::restoreOverrideCursor();
607 608 609 610 611
    }
}

void TagEditor::saveChangesPrompt()
{
612
    if(!isVisible() || !m_dataChanged || m_items.isEmpty())
613
        return;
614 615 616

    QStringList files;

617 618
    foreach(const PlaylistItem *item, m_items)
        files.append(item->file().absFilePath());
619 620

    if(KMessageBox::questionYesNoList(this,
621 622 623
                                      i18n("Do you want to save your changes to:\n"),
                                      files,
                                      i18n("Save Changes"),
Aaron J. Seigo's avatar
Aaron J. Seigo committed
624 625
                                      KStandardGuiItem::save(),
                                      KStandardGuiItem::discard(),
626
                                      "tagEditor_showSaveChangesBox") == KMessageBox::Yes)
627
    {
628
        save(m_items);
629 630 631
    }
}

632 633
void TagEditor::showEvent(QShowEvent *e)
{
634
    if(m_collectionChanged) {
635
        updateCollection();
636
    }
637

638 639 640
    QWidget::showEvent(e);
}

641 642
bool TagEditor::eventFilter(QObject *watched, QEvent *e)
{
643
    QKeyEvent *ke = static_cast<QKeyEvent*>(e);
Tim Beaulen's avatar
Tim Beaulen committed
644
    if(watched->inherits("QSpinBox") && e->type() == QEvent::KeyRelease && ke->modifiers() == 0)
645
        slotDataChanged();
646 647 648 649

    return false;
}

650 651 652 653
////////////////////////////////////////////////////////////////////////////////
// private slots
////////////////////////////////////////////////////////////////////////////////

654
void TagEditor::slotDataChanged(bool c)
655
{
656
    m_dataChanged = c;
657 658
}

659 660
void TagEditor::slotItemRemoved(PlaylistItem *item)
{
661
    m_items.removeAll(item);
662
    if(m_items.isEmpty())
663
        slotRefresh();
664 665
}

666 667 668
void TagEditor::slotPlaylistDestroyed(Playlist *p)
{
    if(m_currentPlaylist == p) {
669 670
        m_currentPlaylist = 0;
        slotSetItems(PlaylistItemList());
671 672 673
    }
}

674
// vim: set et sw=4 tw=0 sta: