Mixxx

/home/maxime/Projets/Mixxx/1.10/mixxx/src/library/cratefeature.cpp

Go to the documentation of this file.
00001 // cratefeature.cpp
00002 // Created 10/22/2009 by RJ Ryan (rryan@mit.edu)
00003 
00004 #include <QInputDialog>
00005 #include <QMenu>
00006 #include <QLineEdit>
00007 
00008 #include "library/cratefeature.h"
00009 #include "library/parser.h"
00010 #include "library/parserm3u.h"
00011 #include "library/parserpls.h"
00012 
00013 #include "library/cratetablemodel.h"
00014 #include "library/trackcollection.h"
00015 #include "widget/wlibrarytextbrowser.h"
00016 #include "widget/wlibrary.h"
00017 #include "widget/wlibrarysidebar.h"
00018 #include "mixxxkeyboard.h"
00019 #include "treeitem.h"
00020 #include "soundsourceproxy.h"
00021 
00022 CrateFeature::CrateFeature(QObject* parent,
00023                            TrackCollection* pTrackCollection, ConfigObject<ConfigValue>* pConfig)
00024         : m_pTrackCollection(pTrackCollection),
00025           m_crateListTableModel(this, pTrackCollection->getDatabase()),
00026           m_pConfig(pConfig),
00027           m_crateTableModel(this, pTrackCollection) {
00028     m_pCreateCrateAction = new QAction(tr("New Crate"),this);
00029     connect(m_pCreateCrateAction, SIGNAL(triggered()),
00030             this, SLOT(slotCreateCrate()));
00031 
00032     m_pDeleteCrateAction = new QAction(tr("Remove"),this);
00033     connect(m_pDeleteCrateAction, SIGNAL(triggered()),
00034             this, SLOT(slotDeleteCrate()));
00035 
00036     m_pRenameCrateAction = new QAction(tr("Rename"),this);
00037     connect(m_pRenameCrateAction, SIGNAL(triggered()),
00038             this, SLOT(slotRenameCrate()));
00039 
00040     m_pLockCrateAction = new QAction(tr("Lock"),this);
00041     connect(m_pLockCrateAction, SIGNAL(triggered()),
00042             this, SLOT(slotToggleCrateLock()));
00043 
00044     m_pImportPlaylistAction = new QAction(tr("Import Crate"),this);
00045     connect(m_pImportPlaylistAction, SIGNAL(triggered()),
00046             this, SLOT(slotImportPlaylist()));
00047     m_pExportPlaylistAction = new QAction(tr("Export Crate"), this);
00048     connect(m_pExportPlaylistAction, SIGNAL(triggered()),
00049             this, SLOT(slotExportPlaylist()));
00050 
00051     m_crateListTableModel.setTable("crates");
00052     m_crateListTableModel.setSort(m_crateListTableModel.fieldIndex("name"),
00053                                   Qt::AscendingOrder);
00054     m_crateListTableModel.setFilter("show = 1");
00055     m_crateListTableModel.select();
00056 
00057     // construct child model
00058     TreeItem *rootItem = new TreeItem();
00059     m_childModel.setRootItem(rootItem);
00060     constructChildModel();
00061 }
00062 
00063 CrateFeature::~CrateFeature() {
00064     //delete QActions
00065     delete m_pCreateCrateAction;
00066     delete m_pDeleteCrateAction;
00067     delete m_pRenameCrateAction;
00068     delete m_pLockCrateAction;
00069     delete m_pImportPlaylistAction;
00070 }
00071 
00072 QVariant CrateFeature::title() {
00073     return tr("Crates");
00074 }
00075 
00076 QIcon CrateFeature::getIcon() {
00077     return QIcon(":/images/library/ic_library_crates.png");
00078 }
00079 
00080 bool CrateFeature::dropAccept(QUrl url) {
00081     return false;
00082 }
00083 
00084 bool CrateFeature::dropAcceptChild(const QModelIndex& index, QUrl url) {
00085     QString crateName = index.data().toString();
00086     int crateId = m_pTrackCollection->getCrateDAO().getCrateIdByName(crateName);
00087 
00088     //XXX: See the comment in PlaylistFeature::dropAcceptChild() about
00089     //     QUrl::toLocalFile() vs. QUrl::toString() usage.
00090     QFileInfo file(url.toLocalFile());
00091 
00092     // Adds track, does not insert duplicates, handles unremoving logic.
00093     int trackId = m_pTrackCollection->getTrackDAO().addTrack(file, true);
00094 
00095     qDebug() << "CrateFeature::dropAcceptChild adding track"
00096              << trackId << "to crate" << crateId;
00097 
00098     CrateDAO& crateDao = m_pTrackCollection->getCrateDAO();
00099 
00100     if (trackId >= 0)
00101         return crateDao.addTrackToCrate(trackId, crateId);
00102     return false;
00103 }
00104 
00105 bool CrateFeature::dragMoveAccept(QUrl url) {
00106     Q_UNUSED(url)
00107     return false;
00108 }
00109 
00110 bool CrateFeature::dragMoveAcceptChild(const QModelIndex& index, QUrl url) {
00111     //TODO: Filter by supported formats regex and reject anything that doesn't match.
00112     QString crateName = index.data().toString();
00113     CrateDAO& crateDao = m_pTrackCollection->getCrateDAO();
00114     int crateId = crateDao.getCrateIdByName(crateName);
00115     bool locked = crateDao.isCrateLocked(crateId);
00116 
00117     QFileInfo file(url.toLocalFile());
00118     bool formatSupported = SoundSourceProxy::isFilenameSupported(file.fileName());
00119     return !locked && formatSupported;
00120 }
00121 
00122 void CrateFeature::bindWidget(WLibrarySidebar* sidebarWidget,
00123                               WLibrary* libraryWidget,
00124                               MixxxKeyboard* keyboard) {
00125     Q_UNUSED(sidebarWidget);
00126     Q_UNUSED(keyboard);
00127     WLibraryTextBrowser* edit = new WLibraryTextBrowser(libraryWidget);
00128     connect(this, SIGNAL(showPage(const QUrl&)),
00129             edit, SLOT(setSource(const QUrl&)));
00130     libraryWidget->registerView("CRATEHOME", edit);
00131 }
00132 
00133 TreeItemModel* CrateFeature::getChildModel() {
00134     return &m_childModel;
00135 }
00136 
00137 void CrateFeature::activate() {
00138     emit(showPage(QUrl("qrc:/html/crates.html")));
00139     emit(switchToView("CRATEHOME"));
00140 }
00141 
00142 void CrateFeature::activateChild(const QModelIndex& index) {
00143     if (!index.isValid())
00144         return;
00145     QString crateName = index.data().toString();
00146     int crateId = m_pTrackCollection->getCrateDAO().getCrateIdByName(crateName);
00147     m_crateTableModel.setCrate(crateId);
00148     emit(showTrackModel(&m_crateTableModel));
00149 }
00150 
00151 void CrateFeature::onRightClick(const QPoint& globalPos) {
00152     m_lastRightClickedIndex = QModelIndex();
00153     QMenu menu(NULL);
00154     menu.addAction(m_pCreateCrateAction);
00155     menu.exec(globalPos);
00156 }
00157 
00158 void CrateFeature::onRightClickChild(const QPoint& globalPos, QModelIndex index) {
00159     //Save the model index so we can get it in the action slots...
00160     m_lastRightClickedIndex = index;
00161 
00162     QString crateName = index.data().toString();
00163     CrateDAO& crateDAO = m_pTrackCollection->getCrateDAO();
00164     int crateId = crateDAO.getCrateIdByName(crateName);
00165 
00166     bool locked = crateDAO.isCrateLocked(crateId);
00167 
00168     m_pDeleteCrateAction->setEnabled(!locked);
00169     m_pRenameCrateAction->setEnabled(!locked);
00170 
00171     m_pLockCrateAction->setText(locked ? tr("Unlock") : tr("Lock"));
00172 
00173     QMenu menu(NULL);
00174     menu.addAction(m_pCreateCrateAction);
00175     menu.addSeparator();
00176     menu.addAction(m_pRenameCrateAction);
00177     menu.addAction(m_pDeleteCrateAction);
00178     menu.addAction(m_pLockCrateAction);
00179     menu.addSeparator();
00180     menu.addAction(m_pImportPlaylistAction);
00181     menu.addAction(m_pExportPlaylistAction);
00182     menu.exec(globalPos);
00183 }
00184 
00185 void CrateFeature::slotCreateCrate() {
00186 
00187     QString name;
00188     bool validNameGiven = false;
00189     CrateDAO& crateDao = m_pTrackCollection->getCrateDAO();
00190 
00191     do {
00192         bool ok = false;
00193         name = QInputDialog::getText(NULL,
00194                                      tr("New Crate"),
00195                                      tr("Crate name:"),
00196                                      QLineEdit::Normal, tr("New Crate"),
00197                                      &ok).trimmed();
00198 
00199         if (!ok)
00200             return;
00201 
00202         int existingId = crateDao.getCrateIdByName(name);
00203 
00204         if (existingId != -1) {
00205             QMessageBox::warning(NULL,
00206                                  tr("Creating Crate Failed"),
00207                                  tr("A crate by that name already exists."));
00208         }
00209         else if (name.isEmpty()) {
00210             QMessageBox::warning(NULL,
00211                                  tr("Creating Crate Failed"),
00212                                  tr("A crate cannot have a blank name."));
00213         }
00214         else {
00215             validNameGiven = true;
00216         }
00217 
00218     } while (!validNameGiven);
00219 
00220     bool crateCreated = crateDao.createCrate(name);
00221 
00222     if (crateCreated) {
00223         clearChildModel();
00224         m_crateListTableModel.select();
00225         constructChildModel();
00226         // Switch to the new crate.
00227         int crate_id = crateDao.getCrateIdByName(name);
00228         m_crateTableModel.setCrate(crate_id);
00229         emit(showTrackModel(&m_crateTableModel));
00230         // TODO(XXX) set sidebar selection
00231         emit(featureUpdated());
00232     } else {
00233         qDebug() << "Error creating crate with name " << name;
00234         QMessageBox::warning(NULL,
00235                              tr("Creating Crate Failed"),
00236                              tr("An unknown error occurred while creating crate: ")
00237                              + name);
00238 
00239     }
00240 }
00241 
00242 void CrateFeature::slotDeleteCrate() {
00243     QString crateName = m_lastRightClickedIndex.data().toString();
00244     CrateDAO &crateDao = m_pTrackCollection->getCrateDAO();
00245     int crateId = crateDao.getCrateIdByName(crateName);
00246     bool locked = crateDao.isCrateLocked(crateId);
00247 
00248     if (locked) {
00249         qDebug() << "Skipping crate deletion because crate" << crateId << "is locked.";
00250         return;
00251     }
00252 
00253     bool deleted = crateDao.deleteCrate(crateId);
00254 
00255     if (deleted) {
00256         clearChildModel();
00257         m_crateListTableModel.select();
00258         constructChildModel();
00259         emit(featureUpdated());
00260     } else {
00261         qDebug() << "Failed to delete crateId" << crateId;
00262     }
00263 }
00264 
00265 void CrateFeature::slotRenameCrate() {
00266     QString oldName = m_lastRightClickedIndex.data().toString();
00267     CrateDAO &crateDao = m_pTrackCollection->getCrateDAO();
00268     int crateId = crateDao.getCrateIdByName(oldName);
00269     bool locked = crateDao.isCrateLocked(crateId);
00270 
00271     if (locked) {
00272         qDebug() << "Skipping crate rename because crate" << crateId << "is locked.";
00273         return;
00274     }
00275 
00276     QString newName;
00277     bool validNameGiven = false;
00278 
00279     do {
00280         bool ok = false;
00281         newName = QInputDialog::getText(NULL,
00282                                         tr("Rename Crate"),
00283                                         tr("New crate name:"),
00284                                         QLineEdit::Normal,
00285                                         oldName,
00286                                         &ok).trimmed();
00287 
00288         if (!ok || newName == oldName) {
00289             return;
00290         }
00291 
00292         int existingId = m_pTrackCollection->getCrateDAO().getCrateIdByName(newName);
00293 
00294         if (existingId != -1) {
00295             QMessageBox::warning(NULL,
00296                                 tr("Renaming Crate Failed"),
00297                                 tr("A crate by that name already exists."));
00298         }
00299         else if (newName.isEmpty()) {
00300             QMessageBox::warning(NULL,
00301                                 tr("Renaming Crate Failed"),
00302                                 tr("A crate cannot have a blank name."));
00303         }
00304         else {
00305             validNameGiven = true;
00306         }
00307     } while (!validNameGiven);
00308 
00309 
00310     if (m_pTrackCollection->getCrateDAO().renameCrate(crateId, newName)) {
00311         clearChildModel();
00312         m_crateListTableModel.select();
00313         constructChildModel();
00314         emit(featureUpdated());
00315         m_crateTableModel.setCrate(crateId);
00316     } else {
00317         qDebug() << "Failed to rename crateId" << crateId;
00318     }
00319 }
00320 
00321 void CrateFeature::slotToggleCrateLock()
00322 {
00323     QString crateName = m_lastRightClickedIndex.data().toString();
00324     CrateDAO& crateDAO = m_pTrackCollection->getCrateDAO();
00325     int crateId = crateDAO.getCrateIdByName(crateName);
00326     bool locked = !crateDAO.isCrateLocked(crateId);
00327 
00328     if (!crateDAO.setCrateLocked(crateId, locked)) {
00329         qDebug() << "Failed to toggle lock of crateId " << crateId;
00330     }
00331 
00332     TreeItem* crateItem = m_childModel.getItem(m_lastRightClickedIndex);
00333     crateItem->setIcon(
00334         locked ? QIcon(":/images/library/ic_library_locked.png") : QIcon());
00335 }
00336 
00337 
00343 void CrateFeature::constructChildModel()
00344 {
00345     QList<TreeItem*> data_list;
00346     int nameColumn = m_crateListTableModel.record().indexOf("name");
00347     int idColumn = m_crateListTableModel.record().indexOf("id");
00348     //Access the invisible root item
00349     TreeItem* root = m_childModel.getItem(QModelIndex());
00350     CrateDAO &crateDao = m_pTrackCollection->getCrateDAO();
00351 
00352     for (int row = 0; row < m_crateListTableModel.rowCount(); ++row) {
00353             QModelIndex ind = m_crateListTableModel.index(row, nameColumn);
00354             QString crate_name = m_crateListTableModel.data(ind).toString();
00355             ind = m_crateListTableModel.index(row, idColumn);
00356             int crate_id = m_crateListTableModel.data(ind).toInt();
00357 
00358             //Create the TreeItem whose parent is the invisible root item
00359             TreeItem* item = new TreeItem(crate_name, crate_name, this, root);
00360             bool locked = crateDao.isCrateLocked(crate_id);
00361             item->setIcon(locked ? QIcon(":/images/library/ic_library_locked.png") : QIcon());
00362             data_list.append(item);
00363     }
00364     //Append all the newly created TreeItems in a dynamic way to the childmodel
00365     m_childModel.insertRows(data_list, 0, m_crateListTableModel.rowCount());
00366 }
00367 
00371 void CrateFeature::clearChildModel()
00372 {
00373     m_childModel.removeRows(0,m_crateListTableModel.rowCount());
00374 }
00375 
00376 void CrateFeature::slotImportPlaylist()
00377 {
00378     qDebug() << "slotImportPlaylist() row:" ; //<< m_lastRightClickedIndex.data();
00379 
00380 
00381     QString playlist_file = QFileDialog::getOpenFileName(
00382         NULL,
00383         tr("Import Playlist"),
00384         QDesktopServices::storageLocation(QDesktopServices::MusicLocation),
00385         tr("Playlist Files (*.m3u *.m3u8 *.pls)"));
00386     // Exit method if user cancelled the open dialog.
00387     if (playlist_file.isNull() || playlist_file.isEmpty() ) return;
00388 
00389     Parser* playlist_parser = NULL;
00390 
00391     if (playlist_file.endsWith(".m3u", Qt::CaseInsensitive) ||
00392         playlist_file.endsWith(".m3u8", Qt::CaseInsensitive)) {
00393         // .m3u8 is Utf8 representation of an m3u playlist
00394         playlist_parser = new ParserM3u();
00395     } else if (playlist_file.endsWith(".pls", Qt::CaseInsensitive)) {
00396         playlist_parser = new ParserPls();
00397     } else {
00398         return;
00399     }
00400 
00401     QList<QString> entries = playlist_parser->parse(playlist_file);
00402     //qDebug() << "Size of Imported Playlist: " << entries.size();
00403 
00404     //Iterate over the List that holds URLs of playlist entires
00405     for (int i = 0; i < entries.size(); ++i) {
00406         m_crateTableModel.addTrack(QModelIndex(), entries[i]);
00407         //qDebug() << "Playlist entry: " << entries[i];
00408     }
00409 
00410     //delete the parser object
00411     if(playlist_parser)
00412         delete playlist_parser;
00413 }
00414 
00415 void CrateFeature::onLazyChildExpandation(const QModelIndex &index){
00416     Q_UNUSED(index);
00417     //Nothing to do because the childmodel is not of lazy nature.
00418 }
00419 
00420 void CrateFeature::slotExportPlaylist(){
00421     qDebug() << "Export crate" << m_lastRightClickedIndex.data();
00422     QString file_location = QFileDialog::getSaveFileName(
00423         NULL,
00424         tr("Export Crate"),
00425         QDesktopServices::storageLocation(QDesktopServices::MusicLocation),
00426         tr("M3U Playlist (*.m3u);;M3U8 Playlist (*.m3u8);;PLS Playlist (*.pls)"));
00427     // Exit method if user cancelled the open dialog.
00428     if (file_location.isNull() || file_location.isEmpty())
00429         return;
00430     // check config if relative paths are desired
00431     bool useRelativePath = static_cast<bool>(
00432         m_pConfig->getValueString(
00433             ConfigKey("[Library]", "UseRelativePathOnExport")).toInt());
00434 
00435     // Create and populate a list of files of the crate
00436     QList<QString> playlist_items;
00437     // Create a new table model since the main one might have an active search.
00438     QScopedPointer<CrateTableModel> pCrateTableModel(
00439         new CrateTableModel(this, m_pTrackCollection));
00440     pCrateTableModel->setCrate(m_crateTableModel.getCrate());
00441     pCrateTableModel->select();
00442     int rows = pCrateTableModel->rowCount();
00443     for (int i = 0; i < rows; ++i) {
00444         QModelIndex index = pCrateTableModel->index(i, 0);
00445         playlist_items << pCrateTableModel->getTrackLocation(index);
00446     }
00447 
00448     if (file_location.endsWith(".pls", Qt::CaseInsensitive)) {
00449         ParserPls::writePLSFile(file_location, playlist_items, useRelativePath);
00450     } else if (file_location.endsWith(".m3u8", Qt::CaseInsensitive)) {
00451         ParserM3u::writeM3U8File(file_location, playlist_items,
00452                                  useRelativePath);
00453     } else {
00454         // Default export to M3U if file extension is missing
00455         if (!file_location.endsWith(".m3u", Qt::CaseInsensitive)) {
00456             qDebug() << "Crate export: No valid file extension specified. Appending .m3u "
00457                      << "and exporting to M3U.";
00458             file_location.append(".m3u");
00459         }
00460         ParserM3u::writeM3UFile(file_location, playlist_items, useRelativePath);
00461     }
00462 }
00463 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines