diff options
Diffstat (limited to 'juk/filerenamer.h')
-rw-r--r-- | juk/filerenamer.h | 543 |
1 files changed, 543 insertions, 0 deletions
diff --git a/juk/filerenamer.h b/juk/filerenamer.h new file mode 100644 index 00000000..0097ed86 --- /dev/null +++ b/juk/filerenamer.h @@ -0,0 +1,543 @@ +/*************************************************************************** + 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: |