Commit d9223918 authored by Michael Pyne's avatar Michael Pyne

GUI: Overhaul the file renamer configuration dialog based on a design of...

GUI: Overhaul the file renamer configuration dialog based on a design of Scott's.  This involved a lot more code than I thought at first, but it should be pretty neat.

This also should fix wish 64849 (Add genre to file renamer) and wish 63912.
In essence this creates a dialog that has a row with each possible category, and arrows allowing you to move that category up and down.
* In the options for the category, you can control leading and trailing text, and what happens if the tag is empty.
* You can set a minimum width for the track (bug 63912).
* The dialog includes a sample result, and you can either edit the sample tag values manually, or load tags from a file to see what it would look like.
* Right now the file renamer code automatically suppresses separators between tag values that use bracketing characters, since I feel that stuff like Artist - Title - [Track] looks dumb, but that may be changed/removed later.

Suggestions are accepted: michael (dot) pyne (at) kdemail (dot) net.

BUG:63912
BUG:64849

svn path=/trunk/kdemultimedia/juk/; revision=359516
parent 606a309a
/***************************************************************************
begin : Sun Oct 31 2004
copyright : (C) 2004 by Michael Pyne
email : michael.pyne@kdemail.net
***************************************************************************/
/***************************************************************************
* *
* 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 <qstring.h>
#include "filerenameroptions.h"
#include "categoryreaderinterface.h"
QString CategoryReaderInterface::value(TagType category) const
{
QString value = categoryValue(category).stripWhiteSpace();
if(value.isEmpty() && emptyAction(category) == TagRenamerOptions::UseReplacementValue)
value = emptyText(category);
return prefix(category) + value + suffix(category);
}
bool CategoryReaderInterface::isRequired(TagType category) const
{
return emptyAction(category) != TagRenamerOptions::IgnoreEmptyTag;
}
bool CategoryReaderInterface::isEmpty(TagType category) const
{
return categoryValue(category).isEmpty();
}
// vim: set et sw=4 ts=4:
/***************************************************************************
begin : Sun Oct 31 2004
copyright : (C) 2004 by Michael Pyne
email : michael.pyne@kdemail.net
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#ifndef JUK_CATEGORYREADERINTERFACE_H
#define JUK_CATEGORYREADERINTERFACE_H
#include "tagrenameroptions.h"
class QString;
template<class T> class QValueList;
/**
* This class is used to map categories into values. You should implement the
* functionality in a subclass.
*
* @author Michael Pyne <michael.pyne@kdemail.net>
*/
class CategoryReaderInterface
{
public:
virtual ~CategoryReaderInterface() { }
/**
* Returns the textual representation of \p type, without any processing done
* on it. For example, track values shouldn't be expanded out to the minimum
* width from this function.
*
* @param category to retrieve the value of.
* @return textual representation of that category's value.
*/
virtual QString categoryValue(TagType type) const = 0;
/**
* Returns the user-specified prefix string for \p category.
*
* @param category the category to retrieve the value for.
* @return user-specified prefix string for \p category.
*/
virtual QString prefix(TagType category) const = 0;
/**
* Returns the user-specified suffix string for \p category.
*
* @param category the category to retrieve the value for.
* @return user-specified suffix string for \p category.
*/
virtual QString suffix(TagType category) const = 0;
/**
* Returns the user-specified empty action for \p category.
*
* @param category the category to retrieve the value for.
* @return user-specified empty action for \p category.
*/
virtual TagRenamerOptions::EmptyActions emptyAction(TagType category) const = 0;
/**
* Returns the user-specified empty text for \p category. This text might
* be used to replace an empty value.
*
* @param category the category to retrieve the value for.
* @return the user-specified empty text for \p category.
*/
virtual QString emptyText(TagType category) const = 0;
virtual QValueList<TagType> categoryOrder() const = 0;
// You probably shouldn't reimplement this
virtual QString value(TagType category) const;
virtual QString separator() const = 0;
virtual QString musicDirectory() const = 0;
virtual int trackWidth() const = 0;
virtual bool hasDirSeparator(int index) const = 0;
virtual bool isDisabled(TagType category) const = 0;
// You probably shouldn't reimplement this
virtual bool isRequired(TagType category) const;
// You probably shouldn't reimplement this
virtual bool isEmpty(TagType category) const;
};
#endif /* JUK_CATEGORYREADERINTERFACE_H */
// vim: set et sw=4 ts=4:
/***************************************************************************
begin : Thu Oct 28 2004
copyright : (C) 2004 by Michael Pyne
email : michael.pyne@kdemail.net
***************************************************************************/
/***************************************************************************
* *
* 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 <kurlrequester.h>
#include <qradiobutton.h>
#include <qlayout.h>
#include "exampleoptions.h"
ExampleOptions::ExampleOptions(QWidget *parent) :
ExampleOptionsBase(parent, "example options widget")
{
}
void ExampleOptions::exampleSelectionChanged()
{
if(m_fileTagsButton->isChecked())
emit fileChanged();
else
emit dataChanged();
}
void ExampleOptions::exampleDataChanged()
{
emit dataChanged();
}
void ExampleOptions::exampleFileChanged()
{
emit fileChanged();
}
ExampleOptionsDialog::ExampleOptionsDialog(QWidget *parent) :
QDialog(parent, "example options dialog")
{
QVBoxLayout *l = new QVBoxLayout(this);
m_options = new ExampleOptions(this);
l->addWidget(m_options);
// Forward signals
connect(m_options, SIGNAL(fileChanged()), SLOT(fileModeSelected()));
connect(m_options, SIGNAL(dataChanged()), SIGNAL(dataChanged()));
connect(m_options->m_exampleFile, SIGNAL(urlSelected(const QString &)),
this, SIGNAL(fileChanged(const QString &)));
connect(m_options->m_exampleFile, SIGNAL(returnPressed(const QString &)),
this, SIGNAL(fileChanged(const QString &)));
}
void ExampleOptionsDialog::hideEvent(QHideEvent *)
{
emit signalHidden();
}
void ExampleOptionsDialog::showEvent(QShowEvent *)
{
emit signalShown();
}
void ExampleOptionsDialog::fileModeSelected()
{
emit fileChanged(m_options->m_exampleFile->url());
}
#include "exampleoptions.moc"
// vim: set et sw=4 ts=4:
/***************************************************************************
begin : Thu Oct 28 2004
copyright : (C) 2004 by Michael Pyne
email : michael.pyne@kdemail.net
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#ifndef JUK_EXAMPLEOPTIONS_H
#define JUK_EXAMPLEOPTIONS_H
#include <qdialog.h>
#include "exampleoptionsbase.h"
class ExampleOptions : public ExampleOptionsBase
{
Q_OBJECT
public:
ExampleOptions(QWidget *parent);
protected slots:
virtual void exampleSelectionChanged();
virtual void exampleDataChanged();
virtual void exampleFileChanged();
};
// We're not using KDialog(Base) because this dialog won't have any push
// buttons to close it. It's just a little floating dialog.
class ExampleOptionsDialog : public QDialog
{
Q_OBJECT
public:
ExampleOptionsDialog(QWidget *parent);
const ExampleOptions *widget() const { return m_options; }
protected:
virtual void hideEvent(QHideEvent *);
virtual void showEvent(QShowEvent *);
protected slots:
void fileModeSelected();
signals:
void fileChanged(const QString &);
void dataChanged();
void signalHidden();
void signalShown();
private:
ExampleOptions *m_options;
};
#endif /* JUK_EXAMPLEOPTIONS_H */
// vim: set et sw=4 ts=4:
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* filerenamerconfigdlg.cpp - (c) 2003 Frerich Raabe <raabe@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 "filerenamer.h"
#include "filerenamerconfigdlg.h"
#include "filerenamerconfigdlgwidget.h"
#include "tag.h"
/***************************************************************************
begin : Mon Nov 01 2004
copyright : (C) 2004 by Michael Pyne
: (c) 2003 Frerich Raabe <raabe@kde.org>
email : michael.pyne@kdemail.net
***************************************************************************/
/***************************************************************************
* *
* 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 <kapplication.h>
#include <kconfig.h>
#include <klineedit.h>
#include <klocale.h>
#include <kurlrequester.h>
#include <qcheckbox.h>
#include <qlabel.h>
#include "filerenamer.h"
#include "filerenamerconfigdlg.h"
FileRenamerConfigDlg::FileRenamerConfigDlg(QWidget *parent, const char *name)
: KDialogBase(parent, name, true, i18n("File Renamer Configuration"),
Ok | Cancel, Ok, true)
FileRenamerConfigDlg::FileRenamerConfigDlg(QWidget *parent) :
KDialogBase(parent, "file renamer dialog", true,
i18n("File Renamer Options"), Ok | Cancel),
m_renamerWidget(new FileRenamerWidget(this))
{
m_child = new FileRenamerConfigDlgWidget(this, "child");
m_child->urlReqCurrentFilename->setMode(KFile::File | KFile::ExistingOnly);
connect(m_child->leFilenameScheme, SIGNAL(textChanged(const QString &)),
this, SLOT(stateChanged(const QString &)));
connect(m_child->leTitleToken, SIGNAL(textChanged(const QString &)),
this, SLOT(stateChanged(const QString &)));
connect(m_child->cbNeedTitle, SIGNAL(clicked()), SLOT(stateChanged()));
connect(m_child->leArtistToken, SIGNAL(textChanged(const QString &)),
this, SLOT(stateChanged(const QString &)));
connect(m_child->cbNeedArtist, SIGNAL(clicked()), SLOT(stateChanged()));
connect(m_child->leAlbumToken, SIGNAL(textChanged(const QString &)),
this, SLOT(stateChanged(const QString &)));
connect(m_child->cbNeedAlbum, SIGNAL(clicked()), SLOT(stateChanged()));
connect(m_child->leTrackToken, SIGNAL(textChanged(const QString &)),
this, SLOT(stateChanged(const QString &)));
connect(m_child->cbNeedTrack, SIGNAL(clicked()), SLOT(stateChanged()));
connect(m_child->leCommentToken, SIGNAL(textChanged(const QString &)),
this, SLOT(stateChanged(const QString &)));
connect(m_child->cbNeedComment, SIGNAL(clicked()), SLOT(stateChanged()));
connect(m_child->urlReqCurrentFilename, SIGNAL(textChanged(const QString &)),
this, SLOT(currentFilenameChanged(const QString &)));
setMainWidget(m_child);
loadSettings();
m_renamerWidget->setMinimumSize(400, 300);
resize( 400, 300 );
setMainWidget(m_renamerWidget);
}
void FileRenamerConfigDlg::accept()
{
saveSettings();
KDialogBase::accept();
}
// Make sure the config gets saved.
void FileRenamerConfigDlg::currentFilenameChanged(const QString &curFilename)
{
Tag tag(curFilename);
if(!tag.isValid()) {
m_child->lNewFilename->setText(i18n("Could not read tag from file"));
return;
}
FileRenamer renamer;
m_child->lNewFilename->setText(renamer.rename(curFilename, tag));
}
m_renamerWidget->saveConfig();
void FileRenamerConfigDlg::loadSettings()
{
const FileRenamer::Config cfg(kapp->config());
m_child->leFilenameScheme->setText(cfg.filenameScheme());
m_child->leTitleToken->setText(cfg.getToken(FileRenamer::Title));
m_child->cbNeedTitle->setChecked(cfg.tokenNeedsValue(FileRenamer::Title));
m_child->leArtistToken->setText(cfg.getToken(FileRenamer::Artist));
m_child->cbNeedArtist->setChecked(cfg.tokenNeedsValue(FileRenamer::Artist));
m_child->leAlbumToken->setText(cfg.getToken(FileRenamer::Album));
m_child->cbNeedAlbum->setChecked(cfg.tokenNeedsValue(FileRenamer::Album));
m_child->leTrackToken->setText(cfg.getToken(FileRenamer::Track));
m_child->cbNeedTrack->setChecked(cfg.tokenNeedsValue(FileRenamer::Track));
m_child->leCommentToken->setText(cfg.getToken(FileRenamer::Comment));
m_child->cbNeedComment->setChecked(cfg.tokenNeedsValue(FileRenamer::Comment));
}
void FileRenamerConfigDlg::saveSettings()
{
FileRenamer::Config cfg(kapp->config());
cfg.setFilenameScheme(m_child->leFilenameScheme->text());
cfg.setToken(FileRenamer::Title, m_child->leTitleToken->text());
cfg.setTokenNeedsValue(FileRenamer::Title, m_child->cbNeedTitle->isChecked());
cfg.setToken(FileRenamer::Artist, m_child->leArtistToken->text());
cfg.setTokenNeedsValue(FileRenamer::Artist, m_child->cbNeedArtist->isChecked());
cfg.setToken(FileRenamer::Album, m_child->leAlbumToken->text());
cfg.setTokenNeedsValue(FileRenamer::Album, m_child->cbNeedAlbum->isChecked());
cfg.setToken(FileRenamer::Track, m_child->leTrackToken->text());
cfg.setTokenNeedsValue(FileRenamer::Track, m_child->cbNeedTrack->isChecked());
cfg.setToken(FileRenamer::Comment, m_child->leCommentToken->text());
cfg.setTokenNeedsValue(FileRenamer::Comment, m_child->cbNeedComment->isChecked());
kapp->config()->sync();
}
void FileRenamerConfigDlg::stateChanged(const QString &)
{
if(m_child->urlReqCurrentFilename->url().isEmpty())
return;
saveSettings();
currentFilenameChanged(m_child->urlReqCurrentFilename->url());
KDialogBase::accept();
}
#include "filerenamerconfigdlg.moc"
// vim:ts=4:sw=4:et
// vim: set et sw=4 ts=4:
/*
* filerenamerconfigdlg.h - (c) 2003 Frerich Raabe <raabe@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.
*/
#ifndef FILERENAMERCONFIGDLG_H
#define FILERENAMERCONFIGDLG_H
/***************************************************************************
begin : Mon Nov 01 2004
copyright : (C) 2004 by Michael Pyne
: (c) 2003 Frerich Raabe <raabe@kde.org>
email : michael.pyne@kdemail.net
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#ifndef JUK_FILERENAMERCONFIGDLG_H
#define JUK_FILERENAMERCONFIGDLG_H
#include <kdialogbase.h>
class FileRenamerConfigDlgWidget;
class FileRenamerWidget;
class FileRenamerConfigDlg : public KDialogBase
{
Q_OBJECT
public:
FileRenamerConfigDlg(QWidget *parent, const char *name = 0);
FileRenamerConfigDlg(QWidget *parent);
protected slots:
virtual void accept();
private slots:
void currentFilenameChanged(const QString &curFilename);
void stateChanged(const QString & = QString::null);
virtual void accept();
private:
void loadSettings();
void saveSettings();
FileRenamerConfigDlgWidget *m_child;
FileRenamerWidget *m_renamerWidget;
};
#endif // FILERENAMERCONFIGDLG_H
// vim:ts=4:sw=4:et
// vim: set et ts=4 sw=4:
This diff is collapsed.
/***************************************************************************
begin : Thu Oct 28 2004
copyright : (C) 2004 by Michael Pyne
email : michael.pyne@kdemail.net
***************************************************************************/
/***************************************************************************
* *
* 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 <klocale.h>
#include <kdebug.h>
#include <knuminput.h>
#include <qlayout.h>
#include <qlabel.h>
#include <qradiobutton.h>
#include <qlineedit.h>
#include <qbuttongroup.h>
#include "filerenameroptions.h"
FileRenamerTagOptions::FileRenamerTagOptions(QWidget *parent,
const TagRenamerOptions &options) :
FileRenamerTagOptionsBase(parent), m_options(options)
{
layout()->setSpacing(KDialog::spacingHint());
layout()->setMargin(0);
m_emptyTagGroup->layout()->setSpacing(KDialog::spacingHint());
m_trackGroup->layout()->setSpacing(KDialog::spacingHint());
m_emptyValueLayout->setSpacing(KDialog::spacingHint());
m_exampleLayout->setSpacing(KDialog::spacingHint());
m_spinLayout->setSpacing(KDialog::spacingHint());
m_widthLayout->setSpacing(KDialog::spacingHint());
m_tagLayout->setSpacing(KDialog::spacingHint());
m_tagFormatGroup->layout()->setSpacing(KDialog::spacingHint());
if(m_options.category() != Track)
m_trackGroup->hide();
QString tagText = m_options.getTagTypeText();
setCaption(caption().arg(tagText));
m_tagFormatGroup->setTitle(m_tagFormatGroup->title().arg(tagText));
m_emptyTagGroup->setTitle(m_emptyTagGroup->title().arg(tagText));
m_description->setText(m_description->text().arg(tagText));
m_tagLabel->setText(m_tagLabel->text().arg(tagText));
m_prefixText->setText(options.prefix());
m_suffixText->setText(options.suffix());
if(options.emptyAction() == TagRenamerOptions::ForceEmptyInclude)
m_includeEmptyButton->setChecked(true);
else if(options.emptyAction() == TagRenamerOptions::UseReplacementValue)
m_useValueButton->setChecked(true);
m_emptyTagValue->setText(options.emptyText());
m_trackWidth->setValue(options.trackWidth());
slotBracketsChanged();
slotEmptyActionChanged();
slotTrackWidthChanged();
}
void FileRenamerTagOptions::slotBracketsChanged()
{
QString tag = m_options.getTagTypeText();
m_options.setPrefix(m_prefixText->text());
m_options.setSuffix(m_suffixText->text());
m_substitution->setText(m_options.prefix() + tag + m_options.suffix());
}
void FileRenamerTagOptions::slotTrackWidthChanged()
{
unsigned width = m_trackWidth->value();
m_options.setTrackWidth(width);
QString singleDigitText = m_singleDigit->text();
singleDigitText.remove(" ->");
QString doubleDigitText = m_doubleDigit->text();
doubleDigitText.remove(" ->");
if(singleDigitText.length() < width) {
QString p;
p.fill('0', width - singleDigitText.length());
singleDigitText.prepend(p);
}
if(doubleDigitText.length() < width) {
QString p;
p.fill('0', width - doubleDigitText.length());
doubleDigitText.prepend(p);
}
m_singleDigitExample->setText(singleDigitText);
m_doubleDigitExample->setText(doubleDigitText);
}
void FileRenamerTagOptions::slotEmptyActionChanged()
{
m_options.setEmptyText(m_emptyTagValue->text());
m_options.setEmptyAction(TagRenamerOptions::IgnoreEmptyTag);
if(m_useValueButton->isChecked())
m_options.setEmptyAction(TagRenamerOptions::UseReplacementValue);
else if(m_includeEmptyButton->isChecked())
m_options.setEmptyAction(TagRenamerOptions::ForceEmptyInclude);
}
TagOptionsDialog::TagOptionsDialog(QWidget *parent, const TagRenamerOptions &options) :
KDialogBase(parent, 0, true, i18n("File Renamer"), Ok | Cancel), m_options(options)
{
loadConfig();
m_widget = new FileRenamerTagOptions(this, m_options);
m_widget->setMinimumSize(400, 200);
setMainWidget(m_widget);
}
void TagOptionsDialog::accept()
{
m_options = m_widget->options();
saveConfig();
KDialogBase::accept();
}
void TagOptionsDialog::loadConfig()
{
// Our m_options may not have been loaded from KConfig, force that to
// happen.
m_options = TagRenamerOptions(m_options.category());
}
void TagOptionsDialog::saveConfig()
{
m_options.saveConfig();
}
#include "filerenameroptions.moc"
// vim: set et ts=4 sw=4:
/***************************************************************************
begin : Thu Oct 28 2004
copyright : (C) 2004 by Michael Pyne
email : michael.pyne@kdemail.net
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#ifndef JUK_FILERENAMEROPTIONS_H
#define JUK_FILERENAMEROPTIONS_H
#include <kdialogbase.h>
#include "filerenameroptionsbase.h"
#include "tagrenameroptions.h"
/**
* Base widget implementing the options for a particular tag type.
*
* @author Michael Pyne <michael.pyne@kdemail.net>
*/
class FileRenamerTagOptions : public FileRenamerTagOptionsBase
{
Q_OBJECT
public:
FileRenamerTagOptions(QWidget *parent, const TagRenamerOptions &options);
const TagRenamerOptions &options() const { return m_options; }
protected slots:
virtual void slotBracketsChanged();
virtual void slotTrackWidthChanged();
virtual void slotEmptyActionChanged();
private:
TagRenamerOptions m_options;
};
/**
* This defines the dialog that actually gets the options from the user.
*
* @author Michael Pyne <michael.pyne@kdemail.net>
*/
class TagOptionsDialog : public KDialogBase
{
Q_OBJECT
public:
TagOptionsDialog(QWidget *parent, const TagRenamerOptions &options);
const TagRenamerOptions &options() const { return m_options; }
protected slots:
virtual void accept();
private:
// Private methods
void loadConfig(); // Loads m_options from KConfig
void saveConfig(); // Saves m_options to KConfig
// Private members
FileRenamerTagOptions *m_widget;
TagRenamerOptions m_options;
};
#endif /* JUK_FILERENAMEROPTIONS_H */
// vim: set et ts=4 sw=4:
This diff is collapsed.
......@@ -692,11 +692,7 @@ void Playlist::slotRenameFile()
emit signalEnableDirWatch(false);