You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
544 lines
18 KiB
544 lines
18 KiB
/***************************************************************************
|
|
begin : Thu Oct 28 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_FILERENAMER_H
|
|
#define JUK_FILERENAMER_H
|
|
|
|
#include <qstring.h>
|
|
#include <qvaluevector.h>
|
|
#include <qmap.h>
|
|
|
|
#include "filerenamerbase.h"
|
|
#include "filerenameroptions.h"
|
|
#include "categoryreaderinterface.h"
|
|
#include "tagrenameroptions.h"
|
|
#include "playlistitem.h"
|
|
|
|
class ExampleOptionsDialog;
|
|
class QCheckBox;
|
|
class QLayout;
|
|
class QLayoutItem;
|
|
class QPushButton;
|
|
class QVBox;
|
|
class PlaylistItem;
|
|
class QSignalMapper;
|
|
|
|
// Used to decide what direction the FileRenamerWidget will move rows in.
|
|
enum MovementDirection { MoveUp, MoveDown };
|
|
|
|
/**
|
|
* This is used by FileRenamerWidget to store information about a particular
|
|
* tag type, including its position, the QFrame holding the information,
|
|
* the up, down, and enable buttons, and the user-selected renaming options.
|
|
*/
|
|
struct Row
|
|
{
|
|
Row() : widget(0), upButton(0), downButton(0), enableButton(0) {}
|
|
|
|
QWidget *widget;
|
|
|
|
QPushButton *upButton, *downButton, *optionsButton, *enableButton;
|
|
|
|
TagRenamerOptions options;
|
|
CategoryID category; // Includes category and a disambiguation id.
|
|
unsigned position; ///< Position in the GUI (0 == top)
|
|
QString name;
|
|
};
|
|
|
|
/**
|
|
* A list of rows, each of which may have its own category options and other
|
|
* associated data. There is no relation between the order of rows in the vector and their
|
|
* GUI layout. Instead, each Row has a position member which indicates what GUI position it
|
|
* takes up. The index into the vector is known as the row identifier (which is unique but
|
|
* not necessarily constant).
|
|
*/
|
|
typedef QValueVector<Row> Rows;
|
|
|
|
/**
|
|
* Holds a list directory separator checkboxes which separate a row. There
|
|
* should always be 1 less than the number of rows in the GUI.
|
|
*
|
|
* Used for ConfigCategoryReader.
|
|
*/
|
|
typedef QValueVector<QCheckBox *> DirSeparatorCheckBoxes;
|
|
|
|
/**
|
|
* Associates a CategoryID combination with a set of options.
|
|
*
|
|
* Used for ConfigCategoryReader
|
|
*/
|
|
typedef QMap<CategoryID, TagRenamerOptions> CategoryOptionsMap;
|
|
|
|
/**
|
|
* An implementation of CategoryReaderInterface that reads the user's settings
|
|
* from the global KConfig configuration object, and reads track information
|
|
* from whatever the given PlaylistItem is. You can assign different
|
|
* PlaylistItems in order to change the returned tag category information.
|
|
*
|
|
* @author Michael Pyne <michael.pyne@kdemail.net>
|
|
*/
|
|
class ConfigCategoryReader : public CategoryReaderInterface
|
|
{
|
|
public:
|
|
// ConfigCategoryReader specific members
|
|
|
|
ConfigCategoryReader();
|
|
|
|
const PlaylistItem *playlistItem() const { return m_currentItem; }
|
|
void setPlaylistItem(const PlaylistItem *item) { m_currentItem = item; }
|
|
|
|
// CategoryReaderInterface reimplementations
|
|
|
|
virtual QString categoryValue(TagType type) const;
|
|
virtual QString prefix(const CategoryID &category) const;
|
|
virtual QString suffix(const CategoryID &category) const;
|
|
virtual TagRenamerOptions::EmptyActions emptyAction(const CategoryID &category) const;
|
|
virtual QString emptyText(const CategoryID &category) const;
|
|
virtual QValueList<CategoryID> categoryOrder() const;
|
|
virtual QString separator() const;
|
|
virtual QString musicFolder() const;
|
|
virtual int trackWidth(unsigned categoryNum) const;
|
|
virtual bool hasFolderSeparator(unsigned index) const;
|
|
virtual bool isDisabled(const CategoryID &category) const;
|
|
|
|
private:
|
|
const PlaylistItem *m_currentItem;
|
|
CategoryOptionsMap m_options;
|
|
QValueList<CategoryID> m_categoryOrder;
|
|
QString m_separator;
|
|
QString m_musicFolder;
|
|
QValueVector<bool> m_folderSeparators;
|
|
};
|
|
|
|
/**
|
|
* This class implements a dialog that allows the user to alter the behavior
|
|
* of the file renamer. It supports 6 different genre types at this point,
|
|
* and it shouldn't be too difficult to extend that in the future if needed.
|
|
* It allows the user to open an external dialog, which will let the user see
|
|
* an example of what their current options will look like, by either allowing
|
|
* the user to type in some sample information, or by loading a file and
|
|
* reading tags from there.
|
|
*
|
|
* It also implements the CategoryReaderInterface in order to implement the
|
|
* example filename functionality.
|
|
*
|
|
* @author Michael Pyne <michael.pyne@kdemail.net>
|
|
*/
|
|
class FileRenamerWidget : public FileRenamerBase, public CategoryReaderInterface
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
FileRenamerWidget(QWidget *parent);
|
|
~FileRenamerWidget();
|
|
|
|
/// Maximum number of total categories the widget will allow.
|
|
static unsigned const MAX_CATEGORIES = 16;
|
|
|
|
/**
|
|
* This function saves all of the category options to the global KConfig
|
|
* object. You must call this manually, FileRenamerWidget doesn't call it
|
|
* automatically so that situations where the user hits "Cancel" work
|
|
* correctly.
|
|
*/
|
|
void saveConfig();
|
|
|
|
protected slots:
|
|
/**
|
|
* This function should be called whenever the example text may need to be
|
|
* changed. For example, when the user selects a different separator or
|
|
* changes the example text, this slot should be called.
|
|
*/
|
|
virtual void exampleTextChanged();
|
|
|
|
/**
|
|
* This function shows the example dialog if it is hidden, and hides the
|
|
* example dialog if it is shown.
|
|
*/
|
|
virtual void toggleExampleDialog();
|
|
|
|
/**
|
|
* This function inserts the currently selected category, so that the
|
|
* user can use duplicate tags in the file renamer.
|
|
*/
|
|
virtual void insertCategory();
|
|
|
|
private:
|
|
/**
|
|
* This function initializes the category options by loading the data from
|
|
* the global KConfig object. This is called automatically in the constructor.
|
|
*/
|
|
void loadConfig();
|
|
|
|
/**
|
|
* This function adds a "Insert Folder separator" checkbox to the end of
|
|
* the current layout. The setting defaults to being unchecked.
|
|
*/
|
|
void addFolderSeparatorCheckbox();
|
|
|
|
/**
|
|
* This function creates a row in the main view for category, appending it
|
|
* to the end. It handles connecting signals to the mapper and such as
|
|
* well.
|
|
*
|
|
* @param category Type of row to append.
|
|
* @return identifier of newly added row.
|
|
*/
|
|
unsigned addRowCategory(TagType category);
|
|
|
|
/**
|
|
* Removes the given row, updating the other rows to have the correct
|
|
* number of categoryNumber.
|
|
*
|
|
* @param id The identifier of the row to remove.
|
|
* @return true if the delete succeeded, false otherwise.
|
|
*/
|
|
bool removeRow(unsigned id);
|
|
|
|
/**
|
|
* Updates the mappings currently set for the row identified by oldId so
|
|
* that they emit newId instead. Does not actually delete the row given
|
|
* by oldId.
|
|
*
|
|
* @param oldId The identifier of the row to change mappings for.
|
|
* @param newId The identifier to use instead.
|
|
*/
|
|
void moveSignalMappings(unsigned oldId, unsigned newId);
|
|
|
|
/**
|
|
* This function sets up the internal view by creating the checkboxes and
|
|
* the rows for each category.
|
|
*/
|
|
void createTagRows();
|
|
|
|
/**
|
|
* Returns the value for \p category by retrieving the tag from m_exampleFile.
|
|
* If \p category is Track, then an appropriate fixup will be applied if needed
|
|
* to match the user's desired minimum width.
|
|
*
|
|
* @param category the category to retrieve the value for.
|
|
* @return the string representation of the value for \p category.
|
|
*/
|
|
QString fileCategoryValue(TagType category) const;
|
|
|
|
/**
|
|
* Returns the value for \p category by reading the user entry for that
|
|
* category. If \p category is Track, then an appropriate fixup will be applied
|
|
* if needed to match the user's desired minimum width.
|
|
*
|
|
* @param category the category to retrieve the value for.
|
|
* @return the string representation of the value for \p category.
|
|
*/
|
|
virtual QString categoryValue(TagType category) const;
|
|
|
|
/**
|
|
* 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(const CategoryID &category) const
|
|
{
|
|
return m_rows[findIdentifier(category)].options.prefix();
|
|
}
|
|
|
|
/**
|
|
* 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(const CategoryID &category) const
|
|
{
|
|
return m_rows[findIdentifier(category)].options.suffix();
|
|
}
|
|
|
|
/**
|
|
* 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(const CategoryID &category) const
|
|
{
|
|
return m_rows[findIdentifier(category)].options.emptyAction();
|
|
}
|
|
|
|
/**
|
|
* 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(const CategoryID &category) const
|
|
{
|
|
return m_rows[findIdentifier(category)].options.emptyText();
|
|
}
|
|
|
|
/**
|
|
* @return list of CategoryIDs corresponding to the user-specified category order.
|
|
*/
|
|
virtual QValueList<CategoryID> categoryOrder() const;
|
|
|
|
/**
|
|
* @return string that separates the tag values in the file name.
|
|
*/
|
|
virtual QString separator() const;
|
|
|
|
/**
|
|
* @return local path to the music folder used to store renamed files.
|
|
*/
|
|
virtual QString musicFolder() const;
|
|
|
|
/**
|
|
* @param categoryNum Zero-based number of category to get results for (if more than one).
|
|
* @return the minimum width of the track category.
|
|
*/
|
|
virtual int trackWidth(unsigned categoryNum) const
|
|
{
|
|
CategoryID id(Track, categoryNum);
|
|
return m_rows[findIdentifier(id)].options.trackWidth();
|
|
}
|
|
|
|
/**
|
|
* @param index, the 0-based index for the folder boundary.
|
|
* @return true if there should be a folder separator between category
|
|
* index and index + 1, and false otherwise. Note that for purposes
|
|
* of this function, only categories that are required or non-empty
|
|
* should count.
|
|
*/
|
|
virtual bool hasFolderSeparator(unsigned index) const;
|
|
|
|
/**
|
|
* @param category The category to get the status of.
|
|
* @return true if \p category is disabled by the user, and false otherwise.
|
|
*/
|
|
virtual bool isDisabled(const CategoryID &category) const
|
|
{
|
|
return m_rows[findIdentifier(category)].options.disabled();
|
|
}
|
|
|
|
/**
|
|
* This moves the widget \p l in the direction given by \p direction, taking
|
|
* care to make sure that the checkboxes are not moved, and that they are
|
|
* enabled or disabled as appropriate for the new layout, and that the up and
|
|
* down buttons are also adjusted as necessary.
|
|
*
|
|
* @param id the identifier of the row to move
|
|
* @param direction the direction to move
|
|
*/
|
|
void moveItem(unsigned id, MovementDirection direction);
|
|
|
|
/**
|
|
* This function actually performs the work of showing the options dialog for
|
|
* \p category.
|
|
*
|
|
* @param category the category to show the options dialog for.
|
|
*/
|
|
void showCategoryOptions(TagType category);
|
|
|
|
/**
|
|
* This function enables or disables the widget in the row identified by \p id,
|
|
* controlled by \p enable. This function also makes sure that checkboxes are
|
|
* enabled or disabled as appropriate if they no longer make sense due to the
|
|
* adjacent category being enabled or disabled.
|
|
*
|
|
* @param id the identifier of the row to change. This is *not* the category to
|
|
* change.
|
|
* @param enable enables the category if true, disables if false.
|
|
*/
|
|
void setCategoryEnabled(int id, bool enable);
|
|
|
|
/**
|
|
* This function enables all of the up buttons.
|
|
*/
|
|
void enableAllUpButtons();
|
|
|
|
/**
|
|
* This function enables all of the down buttons.
|
|
*/
|
|
void enableAllDownButtons();
|
|
|
|
/**
|
|
* This function returns the identifier of the row at \p position.
|
|
*
|
|
* @param position The position to find the identifier of.
|
|
* @return The unique id of the row at \p position.
|
|
*/
|
|
unsigned idOfPosition(unsigned position) const;
|
|
|
|
/**
|
|
* This function returns the identifier of the row in the m_rows index that
|
|
* contains \p category and matches \p categoryNum.
|
|
*
|
|
* @param category the category to find.
|
|
* @return the identifier of the category, or MAX_CATEGORIES if it couldn't
|
|
* be found.
|
|
*/
|
|
unsigned findIdentifier(const CategoryID &category) const;
|
|
|
|
private slots:
|
|
/**
|
|
* This function reads the tags from \p file and ensures that the dialog will
|
|
* use those tags until a different file is selected or dataSelected() is
|
|
* called.
|
|
*
|
|
* @param file the path to the local file to read.
|
|
*/
|
|
virtual void fileSelected(const QString &file);
|
|
|
|
/**
|
|
* This function reads the tags from the user-supplied examples and ensures
|
|
* that the dialog will use those tags until a file is selected using
|
|
* fileSelected().
|
|
*/
|
|
virtual void dataSelected();
|
|
|
|
/**
|
|
* This function brings up a dialog that allows the user to edit the options
|
|
* for \p id.
|
|
*
|
|
* @param id the unique id to bring up the options for.
|
|
*/
|
|
virtual void showCategoryOption(int id);
|
|
|
|
/**
|
|
* This function removes the row identified by id and updates the internal data to be
|
|
* consistent again, by forwarding the call to removeRow().
|
|
* This roundabout way is done due to QSignalMapper.
|
|
*
|
|
* @param id The unique id to update
|
|
*/
|
|
virtual void slotRemoveRow(int id);
|
|
|
|
/**
|
|
* This function moves \p category up in the layout.
|
|
*
|
|
* @param id the unique id of the widget to move up.
|
|
*/
|
|
virtual void moveItemUp(int id);
|
|
|
|
/**
|
|
* This function moves \p category down in the layout.
|
|
*
|
|
* @param id the unique id of the widget to move down.
|
|
*/
|
|
virtual void moveItemDown(int id);
|
|
|
|
/**
|
|
* This slot should be called whenever the example input dialog is shown.
|
|
*/
|
|
virtual void exampleDialogShown();
|
|
|
|
/**
|
|
* This slot should be called whever the example input dialog is hidden.
|
|
*/
|
|
virtual void exampleDialogHidden();
|
|
|
|
private:
|
|
/// This is the frame that holds all of the category widgets and checkboxes.
|
|
QVBox *m_mainFrame;
|
|
|
|
/**
|
|
* This is the meat of the widget, it holds the rows for the user configuration. It is
|
|
* initially created such that m_rows[0] is the top and row + 1 is the row just below.
|
|
* However, this is NOT NECESSARILY true, so don't rely on this. As soon as the user
|
|
* clicks an arrow to move a row then the order will be messed up. Use row.position to
|
|
* determine where the row is in the GUI.
|
|
*
|
|
* @see idOfPosition
|
|
* @see findIdentifier
|
|
*/
|
|
Rows m_rows;
|
|
|
|
/**
|
|
* This holds an array of checkboxes that allow the user to insert folder
|
|
* separators in between categories.
|
|
*/
|
|
DirSeparatorCheckBoxes m_folderSwitches;
|
|
|
|
ExampleOptionsDialog *m_exampleDialog;
|
|
|
|
/// This is true if we're reading example tags from m_exampleFile.
|
|
bool m_exampleFromFile;
|
|
QString m_exampleFile;
|
|
|
|
// Used to map signals from rows to the correct widget.
|
|
QSignalMapper *mapper;
|
|
QSignalMapper *toggleMapper;
|
|
QSignalMapper *upMapper;
|
|
QSignalMapper *downMapper;
|
|
};
|
|
|
|
/**
|
|
* This class contains the backend code to actually implement the file renaming. It performs
|
|
* the function of moving the files from one location to another, constructing the file name
|
|
* based off of the user's options (see ConfigCategoryReader) and of setting folder icons
|
|
* if appropriate.
|
|
*
|
|
* @author Michael Pyne <michael.pyne@kdemail.net>
|
|
*/
|
|
class FileRenamer
|
|
{
|
|
public:
|
|
FileRenamer();
|
|
|
|
/**
|
|
* Renames the filename on disk of the file represented by item according
|
|
* to the user configuration stored in KConfig.
|
|
*
|
|
* @param item The item to rename.
|
|
*/
|
|
void rename(PlaylistItem *item);
|
|
|
|
/**
|
|
* Renames the filenames on disk of the files given in items according to
|
|
* the user configuration stored in KConfig.
|
|
*
|
|
* @param items The items to rename.
|
|
*/
|
|
void rename(const PlaylistItemList &items);
|
|
|
|
/**
|
|
* Returns the file name that would be generated based on the options read from
|
|
* interface, which must implement CategoryReaderInterface. (A whole interface is used
|
|
* so that we can re-use the code to generate filenames from a in-memory GUI and from
|
|
* KConfig).
|
|
*
|
|
* @param interface object to read options/data from.
|
|
*/
|
|
static QString fileName(const CategoryReaderInterface &interface);
|
|
|
|
private:
|
|
/**
|
|
* Sets the folder icon for elements of the destination path for item (if
|
|
* there is not already a folder icon set, and if the folder's name has
|
|
* the album name.
|
|
*/
|
|
void setFolderIcon(const KURL &dst, const PlaylistItem *item);
|
|
|
|
/**
|
|
* Attempts to rename the file from \a src to \a dest. Returns true if the
|
|
* operation succeeded.
|
|
*/
|
|
bool moveFile(const QString &src, const QString &dest);
|
|
};
|
|
|
|
#endif /* JUK_FILERENAMER_H */
|
|
|
|
// vim: set et sw=4 ts=8:
|