/*************************************************************************** begin : Thu Oct 28 2004 copyright : (C) 2004 by Michael Pyne : (c) 2003 Frerich Raabe 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tag.h" #include "filehandle.h" #include "filerenamer.h" #include "exampleoptions.h" #include "playlistitem.h" #include "playlist.h" #include "coverinfo.h" class ConfirmationDialog : public KDialogBase { public: ConfirmationDialog(const TQMap &files, TQWidget *parent = 0, const char *name = 0) : KDialogBase(parent, name, true, i18n("Warning"), Ok | Cancel) { TQVBox *vbox = makeVBoxMainWidget(); TQHBox *hbox = new TQHBox(vbox); TQLabel *l = new TQLabel(hbox); l->setPixmap(SmallIcon("messagebox_warning", 32)); l = new TQLabel(i18n("You are about to rename the following files. " "Are you sure you want to continue?"), hbox); hbox->setStretchFactor(l, 1); TDEListView *lv = new TDEListView(vbox); lv->addColumn(i18n("Original Name")); lv->addColumn(i18n("New Name")); int lvHeight = 0; TQMap::ConstIterator it = files.begin(); for(; it != files.end(); ++it) { TDEListViewItem *i = it.key() != it.data() ? new TDEListViewItem(lv, it.key(), it.data()) : new TDEListViewItem(lv, it.key(), i18n("No Change")); lvHeight += i->height(); } lvHeight += lv->horizontalScrollBar()->height() + lv->header()->height(); lv->setMinimumHeight(TQMIN(lvHeight, 400)); resize(TQMIN(width(), 500), TQMIN(minimumHeight(), 400)); } }; // // Implementation of ConfigCategoryReader // ConfigCategoryReader::ConfigCategoryReader() : CategoryReaderInterface(), m_currentItem(0) { TDEConfigGroup config(TDEGlobal::config(), "FileRenamer"); TQValueList categoryOrder = config.readIntListEntry("CategoryOrder"); unsigned categoryCount[NumTypes] = { 0 }; // Keep track of each category encountered. // Set a default: if(categoryOrder.isEmpty()) categoryOrder << Artist << Album << Title << Track; TQValueList::ConstIterator catIt = categoryOrder.constBegin(); for(; catIt != categoryOrder.constEnd(); ++catIt) { unsigned catCount = categoryCount[*catIt]++; TagType category = static_cast(*catIt); CategoryID catId(category, catCount); m_options[catId] = TagRenamerOptions(catId); m_categoryOrder << catId; } m_folderSeparators.resize(m_categoryOrder.count() - 1, false); TQValueList checkedSeparators = config.readIntListEntry("CheckedDirSeparators"); TQValueList::ConstIterator it = checkedSeparators.constBegin(); for(; it != checkedSeparators.constEnd(); ++it) { unsigned index = static_cast(*it); if(index < m_folderSeparators.count()) m_folderSeparators[index] = true; } m_musicFolder = config.readPathEntry("MusicFolder", "${HOME}/music"); m_separator = config.readEntry("Separator", " - "); } TQString ConfigCategoryReader::categoryValue(TagType type) const { if(!m_currentItem) return TQString(); Tag *tag = m_currentItem->file().tag(); switch(type) { case Track: return TQString::number(tag->track()); case Year: return TQString::number(tag->year()); case Title: return tag->title(); case Artist: return tag->artist(); case Album: return tag->album(); case Genre: return tag->genre(); default: return TQString(); } } TQString ConfigCategoryReader::prefix(const CategoryID &category) const { return m_options[category].prefix(); } TQString ConfigCategoryReader::suffix(const CategoryID &category) const { return m_options[category].suffix(); } TagRenamerOptions::EmptyActions ConfigCategoryReader::emptyAction(const CategoryID &category) const { return m_options[category].emptyAction(); } TQString ConfigCategoryReader::emptyText(const CategoryID &category) const { return m_options[category].emptyText(); } TQValueList ConfigCategoryReader::categoryOrder() const { return m_categoryOrder; } TQString ConfigCategoryReader::separator() const { return m_separator; } TQString ConfigCategoryReader::musicFolder() const { return m_musicFolder; } int ConfigCategoryReader::trackWidth(unsigned categoryNum) const { return m_options[CategoryID(Track, categoryNum)].trackWidth(); } bool ConfigCategoryReader::hasFolderSeparator(unsigned index) const { if(index >= m_folderSeparators.count()) return false; return m_folderSeparators[index]; } bool ConfigCategoryReader::isDisabled(const CategoryID &category) const { return m_options[category].disabled(); } // // Implementation of FileRenamerWidget // FileRenamerWidget::FileRenamerWidget(TQWidget *parent) : FileRenamerBase(parent), CategoryReaderInterface(), m_exampleFromFile(false) { TQLabel *temp = new TQLabel(0); m_exampleText->setPaletteBackgroundColor(temp->paletteBackgroundColor()); delete temp; layout()->setMargin(0); // We'll be wrapped by KDialogBase // This must be created before createTagRows() is called. m_exampleDialog = new ExampleOptionsDialog(this); createTagRows(); loadConfig(); // Add correct text to combo box. m_category->clear(); for(unsigned i = StartTag; i < NumTypes; ++i) { TQString category = TagRenamerOptions::tagTypeText(static_cast(i)); m_category->insertItem(category); } connect(m_exampleDialog, TQT_SIGNAL(signalShown()), TQT_SLOT(exampleDialogShown())); connect(m_exampleDialog, TQT_SIGNAL(signalHidden()), TQT_SLOT(exampleDialogHidden())); connect(m_exampleDialog, TQT_SIGNAL(dataChanged()), TQT_SLOT(dataSelected())); connect(m_exampleDialog, TQT_SIGNAL(fileChanged(const TQString &)), this, TQT_SLOT(fileSelected(const TQString &))); exampleTextChanged(); } void FileRenamerWidget::loadConfig() { TQValueList checkedSeparators; TDEConfigGroup config(TDEGlobal::config(), "FileRenamer"); for(unsigned i = 0; i < m_rows.count(); ++i) m_rows[i].options = TagRenamerOptions(m_rows[i].category); checkedSeparators = config.readIntListEntry("CheckedDirSeparators"); TQValueList::ConstIterator it = checkedSeparators.begin(); for(; it != checkedSeparators.end(); ++it) { unsigned separator = static_cast(*it); if(separator < m_folderSwitches.count()) m_folderSwitches[separator]->setChecked(true); } TQString url = config.readPathEntry("MusicFolder", "${HOME}/music"); m_musicFolder->setURL(url); m_separator->setCurrentText(config.readEntry("Separator", " - ")); } void FileRenamerWidget::saveConfig() { TDEConfigGroup config(TDEGlobal::config(), "FileRenamer"); TQValueList checkedSeparators; TQValueList categoryOrder; for(unsigned i = 0; i < m_rows.count(); ++i) { unsigned rowId = idOfPosition(i); // Write out in GUI order, not m_rows order m_rows[rowId].options.saveConfig(m_rows[rowId].category.categoryNumber); categoryOrder += m_rows[rowId].category.category; } for(unsigned i = 0; i < m_folderSwitches.count(); ++i) if(m_folderSwitches[i]->isChecked() == true) checkedSeparators += i; config.writeEntry("CheckedDirSeparators", checkedSeparators); config.writeEntry("CategoryOrder", categoryOrder); config.writePathEntry("MusicFolder", m_musicFolder->url()); config.writeEntry("Separator", m_separator->currentText()); config.sync(); } FileRenamerWidget::~FileRenamerWidget() { } unsigned FileRenamerWidget::addRowCategory(TagType category) { static TQPixmap up = SmallIcon("go-up"); static TQPixmap down = SmallIcon("go-down"); // Find number of categories already of this type. unsigned categoryCount = 0; for(unsigned i = 0; i < m_rows.count(); ++i) if(m_rows[i].category.category == category) ++categoryCount; Row row; row.category = CategoryID(category, categoryCount); row.position = m_rows.count(); unsigned id = row.position; TQHBox *frame = new TQHBox(m_mainFrame); frame->setPaletteBackgroundColor(frame->paletteBackgroundColor().dark(110)); row.widget = frame; frame->setFrameShape(TQFrame::Box); frame->setLineWidth(1); frame->setMargin(3); m_mainFrame->setStretchFactor(frame, 1); TQVBox *buttons = new TQVBox(frame); buttons->setFrameStyle(TQFrame::Plain | TQFrame::Box); buttons->setLineWidth(1); row.upButton = new KPushButton(buttons); row.downButton = new KPushButton(buttons); row.upButton->setPixmap(up); row.downButton->setPixmap(down); row.upButton->setFlat(true); row.downButton->setFlat(true); upMapper->connect(row.upButton, TQT_SIGNAL(clicked()), TQT_SLOT(map())); upMapper->setMapping(TQT_TQOBJECT(row.upButton), id); downMapper->connect(row.downButton, TQT_SIGNAL(clicked()), TQT_SLOT(map())); downMapper->setMapping(TQT_TQOBJECT(row.downButton), id); TQString labelText = TQString("%1").arg(TagRenamerOptions::tagTypeText(category)); TQLabel *label = new TQLabel(labelText, frame); frame->setStretchFactor(label, 1); label->setAlignment(AlignCenter); TQVBox *options = new TQVBox(frame); row.enableButton = new KPushButton(i18n("Remove"), options); toggleMapper->connect(row.enableButton, TQT_SIGNAL(clicked()), TQT_SLOT(map())); toggleMapper->setMapping(TQT_TQOBJECT(row.enableButton), id); row.optionsButton = new KPushButton(i18n("Options"), options); mapper->connect(row.optionsButton, TQT_SIGNAL(clicked()), TQT_SLOT(map())); mapper->setMapping(TQT_TQOBJECT(row.optionsButton), id); row.widget->show(); m_rows.append(row); // Disable add button if there's too many rows. if(m_rows.count() == MAX_CATEGORIES) m_insertCategory->setEnabled(false); return id; } void FileRenamerWidget::moveSignalMappings(unsigned oldId, unsigned newId) { mapper->setMapping(TQT_TQOBJECT(m_rows[oldId].optionsButton), newId); downMapper->setMapping(TQT_TQOBJECT(m_rows[oldId].downButton), newId); upMapper->setMapping(TQT_TQOBJECT(m_rows[oldId].upButton), newId); toggleMapper->setMapping(TQT_TQOBJECT(m_rows[oldId].enableButton), newId); } bool FileRenamerWidget::removeRow(unsigned id) { if(id >= m_rows.count()) { kdWarning(65432) << "Trying to remove row, but " << id << " is out-of-range.\n"; return false; } if(m_rows.count() == 1) { kdError(65432) << "Can't remove last row of File Renamer.\n"; return false; } // Remove widget. Don't delete it since it appears TQSignalMapper may still need it. m_rows[id].widget->deleteLater(); m_rows[id].widget = 0; m_rows[id].enableButton = 0; m_rows[id].upButton = 0; m_rows[id].optionsButton = 0; m_rows[id].downButton = 0; unsigned checkboxPosition = 0; // Remove first checkbox. // If not the first row, remove the checkbox before it. if(m_rows[id].position > 0) checkboxPosition = m_rows[id].position - 1; // The checkbox is contained within a layout widget, so the layout // widget is the one the needs to die. delete m_folderSwitches[checkboxPosition]->parent(); m_folderSwitches.erase(&m_folderSwitches[checkboxPosition]); // Go through all the rows and if they have the same category and a // higher categoryNumber, decrement the number. Also update the // position identifier. for(unsigned i = 0; i < m_rows.count(); ++i) { if(i == id) continue; // Don't mess with ourself. if((m_rows[id].category.category == m_rows[i].category.category) && (m_rows[id].category.categoryNumber < m_rows[i].category.categoryNumber)) { --m_rows[i].category.categoryNumber; } // Items are moving up. if(m_rows[id].position < m_rows[i].position) --m_rows[i].position; } // Every row after the one we delete will have a different identifier, since // the identifier is simply its index into m_rows. So we need to re-do the // signal mappings for the affected rows. for(unsigned i = id + 1; i < m_rows.count(); ++i) moveSignalMappings(i, i - 1); m_rows.erase(&m_rows[id]); // Make sure we update the buttons of affected rows. m_rows[idOfPosition(0)].upButton->setEnabled(false); m_rows[idOfPosition(m_rows.count() - 1)].downButton->setEnabled(false); // We can insert another row now, make sure GUI is updated to match. m_insertCategory->setEnabled(true); TQTimer::singleShot(0, this, TQT_SLOT(exampleTextChanged())); return true; } void FileRenamerWidget::addFolderSeparatorCheckbox() { TQWidget *temp = new TQWidget(m_mainFrame); TQHBoxLayout *l = new TQHBoxLayout(temp); TQCheckBox *cb = new TQCheckBox(i18n("Insert folder separator"), temp); m_folderSwitches.append(cb); l->addWidget(cb, 0, AlignCenter); cb->setChecked(false); connect(cb, TQT_SIGNAL(toggled(bool)), TQT_SLOT(exampleTextChanged())); temp->show(); } void FileRenamerWidget::createTagRows() { TDEConfigGroup config(TDEGlobal::config(), "FileRenamer"); TQValueList categoryOrder = config.readIntListEntry("CategoryOrder"); if(categoryOrder.isEmpty()) categoryOrder << Artist << Album << Artist << Title << Track; // Setup arrays. m_rows.reserve(categoryOrder.count()); m_folderSwitches.reserve(categoryOrder.count() - 1); mapper = new TQSignalMapper(TQT_TQOBJECT(this), "signal mapper"); toggleMapper = new TQSignalMapper(TQT_TQOBJECT(this), "toggle mapper"); upMapper = new TQSignalMapper(TQT_TQOBJECT(this), "up button mapper"); downMapper = new TQSignalMapper(TQT_TQOBJECT(this), "down button mapper"); connect(mapper, TQT_SIGNAL(mapped(int)), TQT_SLOT(showCategoryOption(int))); connect(toggleMapper, TQT_SIGNAL(mapped(int)), TQT_SLOT(slotRemoveRow(int))); connect(upMapper, TQT_SIGNAL(mapped(int)), TQT_SLOT(moveItemUp(int))); connect(downMapper, TQT_SIGNAL(mapped(int)), TQT_SLOT(moveItemDown(int))); m_mainFrame = new TQVBox(m_mainView->viewport()); m_mainFrame->setMargin(10); m_mainFrame->setSpacing(5); m_mainView->addChild(m_mainFrame); m_mainView->setResizePolicy(TQScrollView::AutoOneFit); // OK, the deal with the categoryOrder variable is that we need to create // the rows in the order that they were saved in (the order given by categoryOrder). // The signal mappers operate according to the row identifier. To find the position of // a row given the identifier, use m_rows[id].position. To find the id of a given // position, use idOfPosition(position). TQValueList::ConstIterator it = categoryOrder.constBegin(); for(; it != categoryOrder.constEnd(); ++it) { if(*it < StartTag || *it >= NumTypes) { kdError(65432) << "Invalid category encountered in file renamer configuration.\n"; continue; } if(m_rows.count() == MAX_CATEGORIES) { kdError(65432) << "Maximum number of File Renamer tags reached, bailing.\n"; break; } TagType i = static_cast(*it); addRowCategory(i); // Insert the directory separator checkbox if this isn't the last // item. TQValueList::ConstIterator dup(it); // Check for last item if(++dup != categoryOrder.constEnd()) addFolderSeparatorCheckbox(); } m_rows.first().upButton->setEnabled(false); m_rows.last().downButton->setEnabled(false); // If we have maximum number of categories already, don't let the user // add more. if(m_rows.count() >= MAX_CATEGORIES) m_insertCategory->setEnabled(false); } void FileRenamerWidget::exampleTextChanged() { // Just use .mp3 as an example if(m_exampleFromFile && (m_exampleFile.isEmpty() || !FileHandle(m_exampleFile).tag()->isValid())) { m_exampleText->setText(i18n("No file selected, or selected file has no tags.")); return; } m_exampleText->setText(FileRenamer::fileName(*this) + ".mp3"); } TQString FileRenamerWidget::fileCategoryValue(TagType category) const { FileHandle file(m_exampleFile); Tag *tag = file.tag(); switch(category) { case Track: return TQString::number(tag->track()); case Year: return TQString::number(tag->year()); case Title: return tag->title(); case Artist: return tag->artist(); case Album: return tag->album(); case Genre: return tag->genre(); default: return TQString(); } } TQString FileRenamerWidget::categoryValue(TagType category) const { if(m_exampleFromFile) return fileCategoryValue(category); const ExampleOptions *example = m_exampleDialog->widget(); switch (category) { case Track: return example->m_exampleTrack->text(); case Year: return example->m_exampleYear->text(); case Title: return example->m_exampleTitle->text(); case Artist: return example->m_exampleArtist->text(); case Album: return example->m_exampleAlbum->text(); case Genre: return example->m_exampleGenre->text(); default: return TQString(); } } TQValueList FileRenamerWidget::categoryOrder() const { TQValueList list; // Iterate in GUI row order. for(unsigned i = 0; i < m_rows.count(); ++i) { unsigned rowId = idOfPosition(i); list += m_rows[rowId].category; } return list; } bool FileRenamerWidget::hasFolderSeparator(unsigned index) const { if(index >= m_folderSwitches.count()) return false; return m_folderSwitches[index]->isChecked(); } void FileRenamerWidget::moveItem(unsigned id, MovementDirection direction) { TQWidget *l = m_rows[id].widget; unsigned bottom = m_rows.count() - 1; unsigned pos = m_rows[id].position; unsigned newPos = (direction == MoveUp) ? pos - 1 : pos + 1; // Item we're moving can't go further down after this. if((pos == (bottom - 1) && direction == MoveDown) || (pos == bottom && direction == MoveUp)) { unsigned idBottomRow = idOfPosition(bottom); unsigned idAboveBottomRow = idOfPosition(bottom - 1); m_rows[idBottomRow].downButton->setEnabled(true); m_rows[idAboveBottomRow].downButton->setEnabled(false); } // We're moving the top item, do some button switching. if((pos == 0 && direction == MoveDown) || (pos == 1 && direction == MoveUp)) { unsigned idTopItem = idOfPosition(0); unsigned idBelowTopItem = idOfPosition(1); m_rows[idTopItem].upButton->setEnabled(true); m_rows[idBelowTopItem].upButton->setEnabled(false); } // This is the item we're swapping with. unsigned idSwitchWith = idOfPosition(newPos); TQWidget *w = m_rows[idSwitchWith].widget; // Update the table of widget rows. std::swap(m_rows[id].position, m_rows[idSwitchWith].position); // Move the item two spaces above/below its previous position. It has to // be 2 spaces because of the checkbox. TQBoxLayout *layout = dynamic_cast(m_mainFrame->layout()); layout->remove(l); layout->insertWidget(2 * newPos, l); // Move the top item two spaces in the opposite direction, for a similar // reason. layout->remove(w); layout->insertWidget(2 * pos, w); layout->invalidate(); TQTimer::singleShot(0, this, TQT_SLOT(exampleTextChanged())); } unsigned FileRenamerWidget::idOfPosition(unsigned position) const { if(position >= m_rows.count()) { kdError(65432) << "Search for position " << position << " out-of-range.\n"; return static_cast(-1); } for(unsigned i = 0; i < m_rows.count(); ++i) if(m_rows[i].position == position) return i; kdError(65432) << "Unable to find identifier for position " << position << endl; return static_cast(-1); } unsigned FileRenamerWidget::findIdentifier(const CategoryID &category) const { for(unsigned index = 0; index < m_rows.count(); ++index) if(m_rows[index].category == category) return index; kdError(65432) << "Unable to find match for category " << TagRenamerOptions::tagTypeText(category.category) << ", number " << category.categoryNumber << endl; return MAX_CATEGORIES; } void FileRenamerWidget::enableAllUpButtons() { for(unsigned i = 0; i < m_rows.count(); ++i) m_rows[i].upButton->setEnabled(true); } void FileRenamerWidget::enableAllDownButtons() { for(unsigned i = 0; i < m_rows.count(); ++i) m_rows[i].downButton->setEnabled(true); } void FileRenamerWidget::showCategoryOption(int id) { TagOptionsDialog *dialog = new TagOptionsDialog(this, m_rows[id].options, m_rows[id].category.categoryNumber); if(dialog->exec() == TQDialog::Accepted) { m_rows[id].options = dialog->options(); exampleTextChanged(); } delete dialog; } void FileRenamerWidget::moveItemUp(int id) { moveItem(static_cast(id), MoveUp); } void FileRenamerWidget::moveItemDown(int id) { moveItem(static_cast(id), MoveDown); } void FileRenamerWidget::toggleExampleDialog() { m_exampleDialog->setShown(!m_exampleDialog->isShown()); } void FileRenamerWidget::insertCategory() { TagType category = TagRenamerOptions::tagFromCategoryText(m_category->currentText()); if(category == Unknown) { kdError(65432) << "Trying to add unknown category somehow.\n"; return; } // We need to enable the down button of the current bottom row since it // can now move down. unsigned idBottom = idOfPosition(m_rows.count() - 1); m_rows[idBottom].downButton->setEnabled(true); addFolderSeparatorCheckbox(); // Identifier of new row. unsigned id = addRowCategory(category); // Set its down button to be disabled. m_rows[id].downButton->setEnabled(false); m_mainFrame->layout()->invalidate(); m_mainView->update(); // Now update according to the code in loadConfig(). m_rows[id].options = TagRenamerOptions(m_rows[id].category); exampleTextChanged(); } void FileRenamerWidget::exampleDialogShown() { m_showExample->setText(i18n("Hide Renamer Test Dialog")); } void FileRenamerWidget::exampleDialogHidden() { m_showExample->setText(i18n("Show Renamer Test Dialog")); } void FileRenamerWidget::fileSelected(const TQString &file) { m_exampleFromFile = true; m_exampleFile = file; exampleTextChanged(); } void FileRenamerWidget::dataSelected() { m_exampleFromFile = false; exampleTextChanged(); } TQString FileRenamerWidget::separator() const { return m_separator->currentText(); } TQString FileRenamerWidget::musicFolder() const { return m_musicFolder->url(); } void FileRenamerWidget::slotRemoveRow(int id) { // Remove the given identified row. if(!removeRow(id)) kdError(65432) << "Unable to remove row " << id << endl; } // // Implementation of FileRenamer // FileRenamer::FileRenamer() { } void FileRenamer::rename(PlaylistItem *item) { PlaylistItemList list; list.append(item); rename(list); } void FileRenamer::rename(const PlaylistItemList &items) { ConfigCategoryReader reader; TQStringList errorFiles; TQMap map; TQMap itemMap; for(PlaylistItemList::ConstIterator it = items.begin(); it != items.end(); ++it) { reader.setPlaylistItem(*it); TQString oldFile = (*it)->file().absFilePath(); TQString extension = (*it)->file().fileInfo().extension(false); TQString newFile = fileName(reader) + "." + extension; if(oldFile != newFile) { map[oldFile] = newFile; itemMap[oldFile] = *it; } } if(itemMap.isEmpty() || ConfirmationDialog(map).exec() != TQDialog::Accepted) return; TDEApplication::setOverrideCursor(TQt::waitCursor); for(TQMap::ConstIterator it = map.begin(); it != map.end(); ++it) { if(moveFile(it.key(), it.data())) { itemMap[it.key()]->setFile(it.data()); itemMap[it.key()]->refresh(); setFolderIcon(it.data(), itemMap[it.key()]); } else errorFiles << i18n("%1 to %2").arg(it.key()).arg(it.data()); processEvents(); } TDEApplication::restoreOverrideCursor(); if(!errorFiles.isEmpty()) KMessageBox::errorList(0, i18n("The following rename operations failed:\n"), errorFiles); } bool FileRenamer::moveFile(const TQString &src, const TQString &dest) { kdDebug(65432) << "Moving file " << src << " to " << dest << endl; if(src == dest) return false; // Escape URL. KURL srcURL = KURL::fromPathOrURL(src); KURL dstURL = KURL::fromPathOrURL(dest); // Clean it. srcURL.cleanPath(); dstURL.cleanPath(); // Make sure it is valid. if(!srcURL.isValid() || !dstURL.isValid()) return false; // Get just the directory. KURL dir = dstURL; dir.setFileName(TQString()); // Create the directory. if(!TDEStandardDirs::exists(dir.path())) if(!TDEStandardDirs::makeDir(dir.path())) { kdError() << "Unable to create directory " << dir.path() << endl; return false; } // Move the file. return TDEIO::NetAccess::file_move(srcURL, dstURL); } void FileRenamer::setFolderIcon(const KURL &dst, const PlaylistItem *item) { if(item->file().tag()->album().isEmpty() || !item->file().coverInfo()->hasCover()) { return; } KURL dstURL = dst; dstURL.cleanPath(); // Split path, and go through each path element. If a path element has // the album information, set its folder icon. TQStringList elements = TQStringList::split("/", dstURL.directory()); TQString path; for(TQStringList::ConstIterator it = elements.begin(); it != elements.end(); ++it) { path.append("/" + (*it)); kdDebug() << "Checking path: " << path << endl; if((*it).find(item->file().tag()->album()) != -1 && !TQFile::exists(path + "/.directory")) { // Seems to be a match, let's set the folder icon for the current // path. First we should write out the file. TQPixmap thumb = item->file().coverInfo()->pixmap(CoverInfo::Thumbnail); thumb.save(path + "/.juk-thumbnail.png", "PNG"); KSimpleConfig config(path + "/.directory"); config.setGroup("Desktop Entry"); if(!config.hasKey("Icon")) { config.writeEntry("Icon", TQString("%1/.juk-thumbnail.png").arg(path)); config.sync(); } return; } } } /** * Returns iterator pointing to the last item enabled in the given list with * a non-empty value (or is required to be included). */ TQValueList::ConstIterator lastEnabledItem(const TQValueList &list, const CategoryReaderInterface &interface) { TQValueList::ConstIterator it = list.constBegin(); TQValueList::ConstIterator last = list.constEnd(); for(; it != list.constEnd(); ++it) { if(interface.isRequired(*it) || (!interface.isDisabled(*it) && !interface.categoryValue((*it).category).isEmpty())) { last = it; } } return last; } TQString FileRenamer::fileName(const CategoryReaderInterface &interface) { const TQValueList categoryOrder = interface.categoryOrder(); const TQString separator = interface.separator(); const TQString folder = interface.musicFolder(); TQValueList::ConstIterator lastEnabled; unsigned i = 0; TQStringList list; TQChar dirSeparator = TQChar(TQDir::separator()); // Use lastEnabled to properly handle folder separators. lastEnabled = lastEnabledItem(categoryOrder, interface); bool pastLast = false; // Toggles to true once we've passed lastEnabled. for(TQValueList::ConstIterator it = categoryOrder.begin(); it != categoryOrder.end(); ++it, ++i) { if(it == lastEnabled) pastLast = true; if(interface.isDisabled(*it)) continue; TQString value = interface.value(*it); // The user can use the folder separator checkbox to add folders, so don't allow // slashes that slip in to accidentally create new folders. Should we filter this // back out when showing it in the GUI? value.replace('/', "%2f"); if(!pastLast && interface.hasFolderSeparator(i)) value.append(dirSeparator); if(interface.isRequired(*it) || !value.isEmpty()) list.append(value); } // Construct a single string representation, handling strings ending in // '/' specially TQString result; for(TQStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); /* Empty */) { result += *it; ++it; // Manually advance iterator to check for end-of-list. // Add separator unless at a directory boundary if(it != list.constEnd() && !(*it).startsWith(dirSeparator) && // Check beginning of next item. !result.endsWith(dirSeparator)) { result += separator; } } return TQString(folder + dirSeparator + result); } #include "filerenamer.moc" // vim: set et sw=4 ts=8: