blob: 52c2ba8334c05ef7e5105e36a5a3c663f83ce7ae [file] [log] [blame]
/**
* projectM-qt -- Qt4 based projectM GUI
* Copyright (C)2003-2004 projectM Team
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* See 'LICENSE.txt' included within this release
*
*/
#include <QtGui>
#include "qprojectm_mainwindow.hpp"
#include "qpresetfiledialog.hpp"
#include "qplaylistfiledialog.hpp"
#include <QTextStream>
#include <QCloseEvent>
#include <QFileDialog>
#include "qplaylistmodel.hpp"
#include "qpresettextedit.hpp"
#include "ui_qprojectm_mainwindow.h"
#include "qprojectmconfigdialog.hpp"
#include "qpreseteditordialog.hpp"
#include "ui_qpreseteditordialog.h"
#include "configfile.hpp"
#include "qxmlplaylisthandler.hpp"
#include "nullable.hpp"
#include "qprojectmwidget.hpp"
class PlaylistWriteFunctor {
public:
PlaylistWriteFunctor(const QVector<QProjectM_MainWindow::PlaylistItemMetaData*>::iterator & begin,
const QVector<QProjectM_MainWindow::PlaylistItemMetaData*>::iterator & end, const QString & desc) :
m_pos(begin), m_end(end), m_desc(desc) {}
inline const QString & playlistDesc() const {
return m_desc;
}
inline bool nextItem(QString & name, QString & url, int & rating, int & breedability) {
if (m_pos == m_end)
return false;
const QProjectM_MainWindow::PlaylistItemMetaData & data = **m_pos;
url = data.url;
rating = data.rating;
name = data.name;
breedability = data.breedability;
m_pos++;
return true;
}
private:
QVector<QProjectM_MainWindow::PlaylistItemMetaData*>::const_iterator m_pos;
QVector<QProjectM_MainWindow::PlaylistItemMetaData*>::const_iterator m_end;
const QString & m_desc;
};
QProjectM_MainWindow::QProjectM_MainWindow ( const std::string & config_file, QMutex * audioMutex)
:m_QPresetFileDialog ( new QPresetFileDialog ( this ) ), ui(0), m_QPlaylistFileDialog
( new QPlaylistFileDialog ( this )), playlistModel(0),
configDialog(0), hHeader(0), vHeader(0), _menuVisible(true), _menuAndStatusBarsVisible(true),
activePresetIndex(new Nullable<long>), playlistItemCounter(0), m_QPresetEditorDialog(0)
{
ui = new Ui::QProjectM_MainWindow();
ui->setupUi ( this );
m_QProjectMWidget = new QProjectMWidget ( config_file, this, audioMutex);
m_timer = new QTimer ( this );
connect ( m_timer, SIGNAL ( timeout() ), m_QProjectMWidget, SLOT ( updateGL() ) );
connect ( ui->lockPresetCheckBox, SIGNAL ( stateChanged ( int ) ),
m_QProjectMWidget, SLOT ( setPresetLock ( int ) ) );
connect ( ui->shuffleEnabledCheckBox, SIGNAL ( stateChanged ( int ) ),
m_QProjectMWidget, SLOT ( setShuffleEnabled ( int ) ) );
connect ( ui->clearPresetList_PushButton, SIGNAL ( pressed() ),
this, SLOT ( clearPlaylist() ) );
connect(ui->tableView, SIGNAL(resized(QResizeEvent *)), this, SLOT(refreshHeaders(QResizeEvent*)));
connect(ui->tableView, SIGNAL(mousePressed(QMouseEvent*, const QModelIndexList &)), this,
SLOT(popupPlaylistContextMenu(QMouseEvent*, const QModelIndexList &)));
connect ( m_QProjectMWidget, SIGNAL ( projectM_Initialized(QProjectM*) ),
this, SLOT ( postProjectM_Initialize() ) );
//connect(this, SIGNAL(dockLocationChanged ( Qt::DockWidgetArea)), SLOT(dockLocationChanged(Qt::DockWidgetArea)));
if (!m_QProjectMWidget->isValid()) {
int ret = QMessageBox::warning(this, tr("projectM cannot be started."),
tr("Your graphics driver or configuration is not supported by projectM. Please contact the developers (carmelo.piccione+projectM@gmail.com or psperl+projectM@gmail.com) with your card and driver information so we can help you get it working."),
QMessageBox::Ok);
exit(-1);
}
m_QProjectMWidget->makeCurrent();
m_QProjectMWidget->setFocus();
setCentralWidget ( m_QProjectMWidget );
m_timer->start ( 0 );
createActions();
createMenus();
createToolBars();
createStatusBar();
readSettings();
readPlaylistSettings();
connect ( ui->tableView, SIGNAL ( activated ( const QModelIndex & ) ),
this, SLOT ( selectPlaylistItem ( const QModelIndex & ) ) );
connect ( ui->tableView, SIGNAL ( clicked ( const QModelIndex & ) ),
this, SLOT ( changePresetAttribute ( const QModelIndex & ) ) );
connect ( ui->presetSearchBarLineEdit, SIGNAL ( textChanged ( const QString& ) ),
this, SLOT ( updateFilteredPlaylist ( const QString& ) ) );
}
void QProjectM_MainWindow::dockLocationChanged(Qt::DockWidgetArea area) {
dockWidgetArea = area;
}
#include <QMouseEvent>
void QProjectM_MainWindow::popupPlaylistContextMenu(QMouseEvent * mouseEvent,const QModelIndexList & items) {
selectedPlaylistIndexes = items;
if (items.empty()) {
mouseEvent->accept();
return;
}
if (mouseEvent->button() == Qt::RightButton) {
QPoint point = mouseEvent->globalPos();
playlistContextMenu->popup(point, ui->actionEdit_this_preset);
mouseEvent->accept();
} else {
mouseEvent->accept();
}
}
void QProjectM_MainWindow::readConfig(const std::string & configFile ) {
QSettings settings ( "projectM", "qprojectM" );
if ( settings.value("FullscreenOnStartup", false).toBool() )
this->setWindowState ( this->windowState() | Qt::WindowFullScreen );
else
this->setWindowState ( this->windowState() & ~Qt::WindowFullScreen );
setMenuVisible(settings.value("MenuOnStartup", false).toBool());
ConfigFile config ( configFile );
int wvw = config.read<int> ( "Window Width", 1024 );
int wvh = config.read<int> ( "Window Height", 768 );
ui->shuffleEnabledCheckBox->setCheckState(qprojectM()->settings().shuffleEnabled ? Qt::Checked : Qt::Unchecked);
this->resize(wvw,wvh);
}
QProjectM_MainWindow::~QProjectM_MainWindow()
{
//this->qprojectMWidget()->mutex()->lock();
for ( QHash<QString, PlaylistItemVector*>::iterator pos = historyHash.begin(); pos != historyHash.end(); ++pos )
{
if ( pos.value() )
delete ( pos.value() );
}
if (ui)
delete ( ui );
if (activePresetIndex)
delete (activePresetIndex);
}
void QProjectM_MainWindow::clearPlaylist()
{
playlistModel->clear();
updatePlaylistUrl(QString());
for ( QHash<QString, PlaylistItemVector*>::iterator pos = historyHash.begin(); pos != historyHash.end(); ++pos )
{
delete ( pos.value() );
}
historyHash.clear();
historyHash.insert ( QString(), new PlaylistItemVector );
playlistItemMetaDataHash.clear();
previousFilter = QString();
activePresetIndex->nullify();
ui->presetSearchBarLineEdit->clear();
}
QProjectM * QProjectM_MainWindow::qprojectM()
{
return m_QProjectMWidget->qprojectM();
}
projectM * QProjectM_MainWindow::GetProjectM()
{
return m_QProjectMWidget->qprojectM();
}
void QProjectM_MainWindow::addPCM(float * buffer, unsigned int bufferSize) {
qprojectM()->pcm()->addPCMfloat(buffer, bufferSize);
}
void QProjectM_MainWindow::updatePlaylistSelection ( bool hardCut, unsigned int index )
{
if ( hardCut )
statusBar()->showMessage ( tr(QString( "*** Hard cut to \"%1\" ***" ).arg(this->qprojectM()->getPresetName(index).c_str()).toStdString().c_str()) , 2000 );
else
statusBar()->showMessage ( tr ( "*** Soft cut to \"%1\" ***" ).arg(this->qprojectM()->getPresetName(index).c_str()).toStdString().c_str(), 2000);
*activePresetIndex = (*historyHash[previousFilter])[index];
}
void QProjectM_MainWindow::selectPlaylistItem ( const QModelIndex & index )
{
if ( index.column() > 0 )
return;
selectPlaylistItem(index.row());
}
void QProjectM_MainWindow::selectPlaylistItem ( int rowIndex)
{
qprojectM()->selectPreset (rowIndex);
*activePresetIndex = rowIndex;
playlistModel->updateItemHighlights();
}
void QProjectM_MainWindow::postProjectM_Initialize()
{
QSettings qSettings("projectM", "qprojectM");
playlistModel = new QPlaylistModel ( *qprojectM(), this );
ui->tableView->setModel ( playlistModel );
/// @bug only do this at startup? fix me
//static bool firstOfRefreshPlaylist = true;
QString url;
//if (firstOfRefreshPlaylist) {
//if (firstOfRefreshPlaylist) {
QString playlistFile;
if ((playlistFile = qSettings.value("PlaylistFile", QString()).toString()) == QString())
url = QString(qprojectM()->settings().presetURL.c_str());
else
url = playlistFile;
if (!playlistModel->readPlaylist(url))
url = QString();
updatePlaylistUrl(url);
refreshPlaylist();
if (!configDialog) {
configDialog = new QProjectMConfigDialog(m_QProjectMWidget->configFile(), m_QProjectMWidget, this);
}
readConfig(m_QProjectMWidget->configFile());
connect ( m_QProjectMWidget->qprojectM(), SIGNAL ( presetSwitchedSignal ( bool,unsigned int ) ),
this, SLOT ( updatePlaylistSelection ( bool,unsigned int ) ) );
connect ( m_QProjectMWidget->qprojectM(), SIGNAL ( presetRatingChangedSignal ( unsigned int,int, PresetRatingType) ),
this, SLOT ( presetRatingChanged( unsigned int,int, PresetRatingType) ));
connect ( m_QProjectMWidget->qprojectM(), SIGNAL ( presetSwitchedSignal ( bool,unsigned int ) ),
playlistModel, SLOT ( updateItemHighlights() ) );
disconnect (m_QProjectMWidget);
connect ( m_QProjectMWidget, SIGNAL ( presetLockChanged ( bool ) ),
playlistModel, SLOT ( updateItemHighlights() ) );
disconnect(ui->presetSearchBarLineEdit);
connect ( ui->presetSearchBarLineEdit, SIGNAL ( textChanged ( const QString& ) ),
playlistModel, SLOT ( updateItemHighlights() ) );
disconnect(ui->tableView);
connect(ui->tableView, SIGNAL(deletesRequested(const QModelIndexList&)),
this, SLOT(removePlaylistItems(const QModelIndexList&)));
connect(ui->tableView, SIGNAL(internalDragAndDropRequested(const QModelIndexList &, const QModelIndex &)),
this, SLOT(dragAndDropPlaylistItems(const QModelIndexList &, const QModelIndex &)));
connect(qprojectMWidget(), SIGNAL(projectM_BeforeDestroy()),
this, SLOT(clearPlaylistModel()), Qt::DirectConnection);
/// @bug hack: shouldn't have to change width for this to work correctly
m_QProjectMWidget->resize(m_QProjectMWidget->size().width()-1, m_QProjectMWidget->size().height());
}
void QProjectM_MainWindow::openPresetEditorDialog(int rowIndex) {
qprojectMWidget()->seizePresetLock();
if (!m_QPresetEditorDialog) {
m_QPresetEditorDialog = new QPresetEditorDialog(qprojectMWidget());
connect(m_QPresetEditorDialog, SIGNAL(presetModified(int)),
this, SLOT(selectPlaylistItem(int)));
}
Q_ASSERT(historyHash.contains(previousFilter));
Q_ASSERT(playlistItemMetaDataHash.contains((*historyHash[previousFilter])[rowIndex]));
const QString presetUrl = playlistItemMetaDataHash[(*historyHash[previousFilter])[rowIndex]].url;
m_QPresetEditorDialog->setPreset(presetUrl, rowIndex);
if (m_QPresetEditorDialog->exec()) {
}
qprojectMWidget()->releasePresetLock();
}
void QProjectM_MainWindow::clearPlaylistModel() {
/// put in a dummy model until projectM is reinitialized
ui->tableView->setModel(new QStandardItemModel(this));
if (playlistModel)
delete(playlistModel);
playlistModel = 0;
}
void QProjectM_MainWindow::dragAndDropPlaylistItems(const QModelIndexList & indices, const QModelIndex & targetIndex) {
/// @bug only first index of the indices passed in is supported!
QModelIndex firstIndex = indices.value(0);
PlaylistItemMetaData metaData = playlistItemMetaDataHash[historyHash[previousFilter]->value(firstIndex.row())];
int newIndex;
if (targetIndex.row() < firstIndex.row()) {
newIndex = targetIndex.row();
}
else if (targetIndex.row() > firstIndex.row())
{
newIndex = targetIndex.row()-1;
}
else
{
// Self drag event ignored
// abort();
return;
}
QModelIndexList singularList;
singularList.append(firstIndex);
removePlaylistItems(singularList);
insertPlaylistItem(metaData, newIndex);
ui->presetPlayListDockWidget->setWindowModified ( true );
}
void QProjectM_MainWindow::setMenuAndStatusBarsVisible(bool visible) {
if (visible) {
if (_menuVisible) {
menuBar()->show();
statusBar()->show();
}
else {
menuBar()->hide();
statusBar()->hide();
}
_menuAndStatusBarsVisible = true;
} else {
if (_menuVisible) {
menuBar()->hide();
statusBar()->hide();
}
else {
menuBar()->hide();
statusBar()->hide();
}
_menuAndStatusBarsVisible = false;
}
}
void QProjectM_MainWindow::readPlaylistSettings() {
const QSettings settings ( "projectM", "qprojectM" );
/// @bug Hack for default values here. Should be auto placed or something elative to main window.
const QPoint playlistPos = settings.value("playlistPos",
QPoint(200,200)).toPoint();
bool playlistDocked = settings.value("playlistDocked", true).toBool();
const QSize playlistWindowSize = settings.value("playlistWindowSize", QSize(300,800)).toSize();
//ui->presetPlayListDockWidget
//ui->presetPlayListDockWidget->set
if (!playlistDocked) {
ui->presetPlayListDockWidget->hide();
ui->presetPlayListDockWidget->setFloating(!playlistDocked);
//ui->presetPlayListDockWidget->setFloating(!playlistDocked);
ui->presetPlayListDockWidget->move(playlistPos);
ui->presetPlayListDockWidget->resize(playlistWindowSize);
} else {
if (!ui->menuBar->isVisible())
ui->presetPlayListDockWidget->hide();
else
ui->presetPlayListDockWidget->show();
ui->presetPlayListDockWidget->hide();
}
}
void QProjectM_MainWindow::setMenuVisible(bool visible) {
if (visible) {
ui->dockWidgetContents->resize(_oldPlaylistSize);
ui->presetPlayListDockWidget->show();
if (_menuAndStatusBarsVisible) {
menuBar()->show();
statusBar()->show();
}
else {
menuBar()->hide();
statusBar()->hide();
}
_menuVisible = true;
} else {
_oldPlaylistSize = ui->dockWidgetContents->size();
// Only hide the playlist when it is attached to the main window.
if (!ui->presetPlayListDockWidget->isFloating())
ui->presetPlayListDockWidget->hide();
menuBar()->hide();
statusBar()->hide();
_menuVisible = false;
}
}
void QProjectM_MainWindow::changePresetAttribute ( const QModelIndex & index )
{
if ( index.column() == 0 )
return;
PlaylistItemVector & lastCache = *historyHash[previousFilter];
const long id = lastCache[index.row()];
ui->presetPlayListDockWidget->setWindowModified ( true );
if (index.column() == 1)
{
/// @bug get rid of hard coded rating boundaries
const int newRating = ( ( playlistModel->data ( index, QPlaylistModel::RatingRole ).toInt() ) % 6 ) +1 ;
playlistModel->setData ( index, newRating, QPlaylistModel::RatingRole );
}
else if (index.column() == 2)
{
/// @bug get rid of hard coded breedability boundaries
const int newBreedability = ( ( playlistModel->data ( index, QPlaylistModel::BreedabilityRole ).toInt() ) % 6 ) +1 ;
playlistItemMetaDataHash[id].breedability = newBreedability;
playlistModel->setData ( index, newBreedability, QPlaylistModel::BreedabilityRole );
}
}
void QProjectM_MainWindow::keyReleaseEvent ( QKeyEvent * e )
{
QModelIndex modelIndex;
switch ( e->key() )
{
case Qt::Key_E:
if (e->modifiers() & Qt::ControlModifier) {
if (activePresetIndex->hasValue())
openPresetEditorDialog(historyHash[previousFilter]->indexOf(activePresetIndex->value()));
} else
e->ignore();
case Qt::Key_L:
if (!(e->modifiers() & Qt::ControlModifier)) {
if ( ui->presetSearchBarLineEdit->hasFocus() )
return;
if (ui->tableView->hasFocus())
return;
}
if ( ui->lockPresetCheckBox->checkState() == Qt::Checked )
{
ui->lockPresetCheckBox->setCheckState ( Qt::Unchecked );
}
else
{
ui->lockPresetCheckBox->setCheckState ( Qt::Checked );
}
// the projectM widget handles the actual lock
//e->ignore();
//m_QProjectMWidget->keyReleaseEvent(e);
return;
case Qt::Key_Y:
if (!(e->modifiers() & Qt::ControlModifier)) {
if ( ui->presetSearchBarLineEdit->hasFocus() )
return;
if (ui->tableView->hasFocus())
return;
}
if ( ui->shuffleEnabledCheckBox->checkState() == Qt::Checked )
{
ui->shuffleEnabledCheckBox->setCheckState ( Qt::Unchecked );
}
else
{
ui->shuffleEnabledCheckBox->setCheckState ( Qt::Checked );
}
// the projectM widget handles the actual shuffle event
return;
case Qt::Key_F1:
return;
//emit(keyPressed m_QProjectMWidget,
case Qt::Key_F:
{
if (!(e->modifiers() & Qt::ControlModifier)) {
if ( ui->presetSearchBarLineEdit->hasFocus() )
return;
if (ui->tableView->hasFocus())
return;
}
bool projectM_HadFocus = qprojectMWidget()->hasFocus();
this->setWindowState ( this->windowState() ^ Qt::WindowFullScreen );
if (projectM_HadFocus || !_menuVisible)
qprojectMWidget()->setFocus();
return;
}
case Qt::Key_B:
if (!(e->modifiers() & Qt::ControlModifier)) {
if ( ui->presetSearchBarLineEdit->hasFocus() )
return;
if (ui->tableView->hasFocus())
return;
}
setMenuAndStatusBarsVisible(!_menuAndStatusBarsVisible);
return;
case Qt::Key_M:
if (!(e->modifiers() & Qt::ControlModifier)) {
if ( ui->presetSearchBarLineEdit->hasFocus() )
return;
if (ui->tableView->hasFocus())
return;
}
setMenuVisible(!_menuVisible);
refreshHeaders();
return;
case Qt::Key_R:
if (!(e->modifiers() & Qt::ControlModifier)) {
if ( ui->presetSearchBarLineEdit->hasFocus() )
return;
if (ui->tableView->hasFocus())
return;
}
return;
default:
break;
}
}
void QProjectM_MainWindow::refreshHeaders(QResizeEvent * event) {
hHeader->setResizeMode ( 0, QHeaderView::Fixed);
hHeader->setResizeMode ( 1, QHeaderView::ResizeToContents);
const int numRatings = qprojectM()->settings().softCutRatingsEnabled ? 2 : 1;
int sizeTotal = 0;
for (int i = 0; i < numRatings; i++) {
// Add 1 to skip the Name column
hHeader->setResizeMode (i+1, QHeaderView::ResizeToContents);
sizeTotal += hHeader->sectionSize(i+1);
}
hHeader->resizeSection(0, ui->tableView->size().width()-20-sizeTotal);
}
bool QProjectM_MainWindow::warnIfPlaylistModified() {
if (ui->presetPlayListDockWidget->isWindowModified()) {
int ret = QMessageBox::warning(this, tr("Warning: unsaved playlist!"),
tr("The open playlist has been modified.\n"
"Do you want to save your changes?"),
QMessageBox::Save | QMessageBox::Discard
| QMessageBox::Cancel,
QMessageBox::Save);
switch (ret) {
case QMessageBox::Save:
savePlaylistButtonClick();
return true;
case QMessageBox::Discard:
return true;
case QMessageBox::Cancel:
return false;
default:
return true;
}
}
return true;
}
void QProjectM_MainWindow::closeEvent ( QCloseEvent *event )
{
if (!warnIfPlaylistModified())
event->ignore();
writeSettings();
}
void QProjectM_MainWindow::insertPresetsDialog() {
if (selectedPlaylistIndexes.empty())
return;
return addPresetsDialog(selectedPlaylistIndexes[0]);
}
void QProjectM_MainWindow::addPresetsDialog() {
return addPresetsDialog(QModelIndex());
}
void QProjectM_MainWindow::addPresetsDialog(const QModelIndex & index)
{
/// @bug this probably isn't thread safe
if ( m_QPresetFileDialog->exec() )
{
const QStringList & files = m_QPresetFileDialog->selectedFiles();
int i = 0;
for ( QStringList::const_iterator pos = files.begin();
pos != files.end(); ++pos )
{
if ( *pos != "" ) {
Nullable<int> row;
if (index != QModelIndex() && index.isValid()) {
if (index.row() == 0)
row = 0;
else
row = index.row()-1;
}
loadFile ( *pos, 3, 3, row);
i++;
}
}
PlaylistItemVector * playlistItems = historyHash.value ( QString() );
for ( QHash<QString, PlaylistItemVector*>::iterator pos = historyHash.begin(); pos != historyHash.end(); ++pos )
{
if ( pos.key() != QString() )
delete ( pos.value() );
}
historyHash.clear();
historyHash.insert ( QString(), playlistItems );
updateFilteredPlaylist ( previousFilter );
ui->presetPlayListDockWidget->setWindowModified ( true );
}
//playlistModel->setHeaderData(0, Qt::Horizontal, tr("Preset"));//, Qt::DisplayRole);
}
void QProjectM_MainWindow::savePlaylist()
{
//m_currentPlaylistUrl = file;
if ( m_currentPlaylistUrl == QString() )
{
qDebug() << "current playlist file null!" ;
return;
}
/// @idea add ability to save filtered list
#if 0
if ( playlistModel->writePlaylist ( m_currentPlaylistUrl ) ) {
this->ui->statusbar->showMessage ( QString ( "Saved cropped preset playlist \"%1\" successfully." ).arg ( m_currentPlaylistUrl ), 3000 );
this->ui->presetPlayListDockWidget->setWindowModified ( false );
}
#endif
QFile qfile(m_currentPlaylistUrl);
if (!qfile.open(QIODevice::WriteOnly)) {
QMessageBox::warning (0, "Playlist Save Error", QString("There was a problem trying to save the playlist \"%1\". You may not have permission to modify this file.").arg(m_currentPlaylistUrl));
return ;
}
// Use the hash that maps "" to playlist items since this list contains the entire playlist item set
PlaylistItemVector * playlistItemKeys = historyHash.value(QString(), 0);
QVector<PlaylistItemMetaData*> itemMetaDataVector;
foreach (long key, *playlistItemKeys) {
itemMetaDataVector.push_back(&playlistItemMetaDataHash[key]);
}
PlaylistWriteFunctor writeFunctor(itemMetaDataVector.begin(), itemMetaDataVector.end(), playlistModel->playlistDesc());
QXmlPlaylistHandler::writePlaylist(&qfile, writeFunctor);
this->ui->statusbar->showMessage ( QString ( "Saved preset playlist \"%1\" successfully." ).arg ( m_currentPlaylistUrl ), 4000 );
this->ui->presetPlayListDockWidget->setWindowModified ( false );
}
void QProjectM_MainWindow::updatePlaylistUrl(const QString & url = QString()) {
m_currentPlaylistUrl = url;
if (url == QString()) {
ui->presetPlayListDockWidget->setWindowTitle ( "Preset Playlist - Untitled [*]" );
ui->presetSavePushButton->setEnabled(false);
}
else if (QFileInfo(url).isDir()) {
ui->presetPlayListDockWidget->setWindowTitle
( QString ( "Preset Directory - %1 [*]" ).arg ( url ) );
ui->presetSavePushButton->setEnabled(true);
}
else {
ui->presetPlayListDockWidget->setWindowTitle
( QString ( "Preset Playlist - %1 [*]" ).arg ( QFileInfo(url).fileName() ) );
ui->presetSavePushButton->setEnabled(true);
}
ui->presetPlayListDockWidget->setWindowModified ( false );
}
void QProjectM_MainWindow::savePlaylistAsDialog()
{
m_QPlaylistFileDialog->setAllowDirectorySelect(false);
m_QPlaylistFileDialog->setAllowFileSelect(true);
m_QPlaylistFileDialog->setPlaylistSaveMode(true);
if ( m_QPlaylistFileDialog->exec() ) {
const QStringList & files = m_QPlaylistFileDialog->selectedFiles();
if (files.empty())
return;
QString url = files[0];
if (url != QString()) {
updatePlaylistUrl(url);
savePlaylist();
}
}
}
void QProjectM_MainWindow::savePlaylistButtonClick()
{
if (m_currentPlaylistUrl == QString() || QFileInfo(m_currentPlaylistUrl).isDir())
return savePlaylistAsDialog();
else {
savePlaylist();
}
}
void QProjectM_MainWindow::openPlaylistDialog()
{
m_QPlaylistFileDialog->setAllowDirectorySelect(true);
m_QPlaylistFileDialog->setAllowFileSelect(true);
m_QPlaylistFileDialog->setPlaylistSaveMode(false);
if ( m_QPlaylistFileDialog->exec() )
{
if (m_QPlaylistFileDialog->selectedFiles().empty())
return;
QString searchText = ui->presetSearchBarLineEdit->text();
clearPlaylist();
QString url = m_QPlaylistFileDialog->selectedFiles() [0];
const bool loadedOk = playlistModel->readPlaylist ( url );
if (!loadedOk) {
qDebug() << "could not open playlist";
url = QString();
}
qDebug() << "url: " << url;
updatePlaylistUrl(url);
copyPlaylist();
ui->presetSearchBarLineEdit->setText(searchText);
updateFilteredPlaylist ( ui->presetSearchBarLineEdit->text() );
}
}
void QProjectM_MainWindow::copyPlaylist()
{
qprojectMWidget()->seizePresetLock();
playlistItemMetaDataHash.clear();
PlaylistItemVector * items = new PlaylistItemVector();
for ( long i = 0; i < playlistModel->rowCount(); i++ )
{
QModelIndex index = playlistModel->index ( i, 0 );
const QString & url = playlistModel->data ( index,
QPlaylistModel::URLInfoRole ).toString();
const QString & name = playlistModel->data ( index, Qt::DisplayRole ).toString();
int rating = playlistModel->data ( index, QPlaylistModel::RatingRole ).toInt();
int breed = playlistModel->data ( index, QPlaylistModel::BreedabilityRole).toInt();
items->push_back (playlistItemCounter );
playlistItemMetaDataHash[playlistItemCounter] =
PlaylistItemMetaData ( url, name, rating, breed, playlistItemCounter );
playlistItemCounter++;
}
historyHash.insert ( QString(), items );
uint index;
if (qprojectM()->selectedPresetIndex(index))
*activePresetIndex = index;
else
activePresetIndex->nullify();
qprojectMWidget()->releasePresetLock();
}
void QProjectM_MainWindow::removeSelectedPlaylistItems() {
removePlaylistItems(selectedPlaylistIndexes);
}
void QProjectM_MainWindow::removePlaylistItems(const QModelIndexList & items) {
qprojectMWidget()->seizePresetLock();
QMap<int, QModelIndex> sortedItems;
QList<int> reverseOrderKeys;
foreach (QModelIndex index, items) {
sortedItems[index.row()] = index;
}
foreach (int key, sortedItems.keys()) {
reverseOrderKeys.insert(0, key);
}
PlaylistItemVector & lastCachedItems = *historyHash[previousFilter];
assert (lastCachedItems.size() == playlistModel->rowCount());
QVector<long> zombieItems;
int i = 0;
foreach (long id, lastCachedItems) {
if (sortedItems.contains(i)) {
zombieItems.push_back(id);
playlistItemMetaDataHash.remove(id);
if (activePresetIndex->hasValue() && id == activePresetIndex->value())
activePresetIndex->nullify();
}
i++;
}
foreach (QString filter, historyHash.keys()) {
PlaylistItemVector & cachedItems = *historyHash.value(filter);
foreach(long id, zombieItems) {
int index = cachedItems.indexOf(id);
if (index >= 0)
cachedItems.remove(index);
}
}
foreach (int key, reverseOrderKeys) {
playlistModel->removeRow(key);
}
qprojectMWidget()->releasePresetLock();
ui->presetPlayListDockWidget->setWindowModified ( true );
}
void QProjectM_MainWindow::insertPlaylistItem
(const PlaylistItemMetaData & data, int targetIndex) {
qprojectMWidget()->seizePresetLock();
const long targetId = historyHash[previousFilter]->value(targetIndex);
playlistItemMetaDataHash[data.id] = data;
foreach(PlaylistItemVector * items, historyHash.values()) {
int insertIndex = items->indexOf(targetId);
items->insert(insertIndex, data.id);
}
playlistModel->insertRow(targetIndex, data.url, data.name, data.rating, data.breedability);
qprojectMWidget()->releasePresetLock();
}
void QProjectM_MainWindow::refreshPlaylist()
{
copyPlaylist();
if (hHeader)
disconnect(hHeader);
hHeader = new QHeaderView ( Qt::Horizontal, this );
vHeader = new QHeaderView ( Qt::Vertical, this );
hHeader->setClickable ( false );
hHeader->setSortIndicatorShown ( false );
ui->tableView->setVerticalHeader ( vHeader );
ui->tableView->setHorizontalHeader ( hHeader );
refreshHeaders();
vHeader->hide();
}
void QProjectM_MainWindow::about()
{
QMessageBox::about ( this, tr ( "About projectM-qt and projectM" ),
tr ( "<p><b>projectM-qt</b> provides useful gui extensions to the projectM core library. For problems please email Carmelo Piccione: \n<a href=\"mailto:carmelo.piccione+projectM@gmail.com\"> carmelo.piccione+projectM@gmail.com</a>.</p><p><b>projectM</b> is an advanced opensource music visualizer originally based on Milkdrop but over time has gone above and beyond it. For more info visit us at <a href=\"http://projectm.sf.net\">projectm.sf.net</a> or join #projectM on the freenode IRC network." ) );
}
void QProjectM_MainWindow::hotkeyReference()
{
QMessageBox::information ( this, tr( "Hotkey Reference"),
tr ( "<dl><dt><b>CTRL-L</b></dt> <dd>Locks / unlocks the active preset</dd><dt><b>CTRL-N</b></dt> <dd>Moves to next preset in the playlist </dd><dt><dt><b>CTRL-P</b></dt> <dd>Moves to previous preset in the playlist </dd><dt><b>CTRL-R</b></dt> <dd>Randomly selects a preset from the playlist </dd><dt><b>CTRL-M</b></dt><dd>Show / hide the menu display</dd><dt><b>CTRL-E</b></dt> <dd> Opens the preset editor with the active preset</dd><dt><b>CTRL-F</b></dt> <dd> Enables / disables full screen mode</dd><dt><b>CTRL-Y</b></dt><dd>Enables / disables shuffle mode </dd><dt><b>CTRL-S</b></dt> <dd>Saves and applies changes to the active preset (only when editing) </dd><dt><b>CTRL-B</b></dt><dd>Shows / hides menu and status bar</dd></dl>" ));
}
void QProjectM_MainWindow::openSettingsDialog() {
if (configDialog->exec()) {
}
}
void QProjectM_MainWindow::aboutQt() {
return QApplication::aboutQt();
}
void QProjectM_MainWindow::createActions()
{
connect ( ui->actionExit, SIGNAL ( triggered() ), this, SLOT ( close() ) );
connect ( ui->actionAddPresets, SIGNAL ( triggered() ), this, SLOT ( addPresetsDialog() ) );
connect ( ui->actionOpen_Play_List, SIGNAL ( triggered() ), this, SLOT ( openPlaylistDialog() ) );
connect ( ui->actionSave_play_list, SIGNAL ( triggered() ), this, SLOT ( savePlaylistButtonClick() ) );
connect ( ui->actionSave_play_list_as, SIGNAL ( triggered() ), this, SLOT ( savePlaylistAsDialog() ) );
connect ( ui->actionAbout_projectM, SIGNAL ( triggered() ), this, SLOT ( about() ) );
connect ( ui->actionConfigure_projectM, SIGNAL ( triggered() ), this, SLOT (openSettingsDialog()) );
connect ( ui->actionAbout_Qt, SIGNAL(triggered()), this, SLOT(aboutQt()));
connect ( ui->actionHotkey_Reference, SIGNAL(triggered()), this, SLOT(hotkeyReference()));
connect (ui->actionRemove_selection, SIGNAL(triggered()), this, SLOT(removeSelectedPlaylistItems()));
connect (ui->actionInsert_presets, SIGNAL(triggered()), this, SLOT(insertPresetsDialog()));
connect (ui->actionEdit_this_preset, SIGNAL(triggered()), this, SLOT(openPresetEditorDialogForSelectedPreset()));
connect (ui->actionHard_cut_to_this_preset, SIGNAL(triggered()), this, SLOT(presetHardCut()));
connect (ui->actionSoft_cut_to_this_preset, SIGNAL(triggered()), this, SLOT(presetSoftCut()));
}
void QProjectM_MainWindow::presetHardCut() {
if (selectedPlaylistIndexes.empty())
return;
qprojectM()->selectPreset(selectedPlaylistIndexes[0].row(), true);
}
void QProjectM_MainWindow::presetSoftCut() {
if (selectedPlaylistIndexes.empty())
return;
qprojectM()->selectPreset(selectedPlaylistIndexes[0].row(), false);
}
void QProjectM_MainWindow::openPresetEditorDialogForSelectedPreset() {
if (selectedPlaylistIndexes.empty())
return;
openPresetEditorDialog(selectedPlaylistIndexes[0].row());
}
void QProjectM_MainWindow::registerSettingsAction(QAction * action) {
ui->menuSettings->addAction(action);
}
void QProjectM_MainWindow::unregisterSettingsAction(QAction * action) {
ui->menuSettings->removeAction(action);
}
void QProjectM_MainWindow::createMenus()
{
playlistContextMenu = new QMenu("Playlist Actions", this);
playlistContextMenu->addAction(ui->actionEdit_this_preset);
playlistContextMenu->addAction(ui->actionInsert_presets);
playlistContextMenu->addAction(ui->actionRemove_selection);
playlistContextMenu->addAction(ui->actionHard_cut_to_this_preset);
playlistContextMenu->addAction(ui->actionSoft_cut_to_this_preset);
ui->menuBar->hide();
}
void QProjectM_MainWindow::createToolBars()
{
}
void QProjectM_MainWindow::createStatusBar()
{
statusBar()->hide();
statusBar()->showMessage ( tr ( "Welcome to projectM!" ) );
}
void QProjectM_MainWindow::readSettings()
{
// The settings exclusively due to qprojectM
QSettings settings ( "projectM", "qprojectM" );
QPoint pos = settings.value ( "pos", QPoint ( 200, 200 ) ).toPoint();
//QSize size = settings.value ( "size", QSize ( 1024, 768 ) ).toSize();
// Load up where the playlist file dialog last was visited last time
m_QPlaylistFileDialog->setDirectory
( settings.value ( "playlistPath", m_QPlaylistFileDialog->directory().absolutePath() ).toString() );
//resize ( size().width(), size().height() );
move ( pos );
}
void QProjectM_MainWindow::writeSettings()
{
QSettings settings ( "projectM", "qprojectM" );
settings.setValue ( "pos", pos() );
//settings.setValue ( "size", size() );
settings.setValue ( "playlistPath", m_QPlaylistFileDialog->directory().absolutePath() );
if (m_currentPlaylistUrl != QString())
settings.setValue("PlaylistFile", m_currentPlaylistUrl);
const QPoint playlistPos = ui->presetPlayListDockWidget->pos();
const bool playlistDocked = !ui->presetPlayListDockWidget->isFloating();
settings.setValue("playlistDocked", playlistDocked);
settings.setValue("playlistPos", playlistPos);
settings.setValue("playlistWindowSize", ui->presetPlayListDockWidget->size());
settings.setValue("playlistDockLocation", dockWidgetArea);
}
void QProjectM_MainWindow::loadFile ( const QString &fileName, int rating, int breed, const Nullable<int> & row)
{
const QString & name = strippedName ( fileName );
PlaylistItemVector * playlistItems = historyHash.value ( QString(), 0 );
Q_ASSERT ( playlistItems != 0 );
if (playlistItems->empty())
ui->presetSavePushButton->setEnabled(true);
playlistItemMetaDataHash[playlistItemCounter] =
PlaylistItemMetaData ( fileName, name, rating, breed, playlistItemCounter) ;
if (row.hasValue())
playlistItems->insert(row.value(), playlistItemCounter);
else
playlistItems->push_back (playlistItemCounter);
playlistItemCounter++;
}
QString QProjectM_MainWindow::strippedName ( const QString &fullFileName )
{
return QFileInfo ( fullFileName ).fileName();
}
void QProjectM_MainWindow::updateFilteredPlaylist ( const QString & text )
{
qprojectMWidget()->seizePresetLock();
const QString filter = text.toLower();
unsigned int presetIndexBackup ;
bool presetSelected = qprojectM()->selectedPresetIndex(presetIndexBackup);
Nullable<unsigned int> activePresetId;
if (!presetSelected && activePresetIndex->hasValue()) {
activePresetId = activePresetIndex->value();
} else if (presetSelected) {
const PlaylistItemVector & oldPlaylistItems = *historyHash.value(previousFilter);
if ((presetIndexBackup >=0) && (presetIndexBackup < oldPlaylistItems.size())) {
activePresetId = oldPlaylistItems[presetIndexBackup];
}
} else {
}
playlistModel->clearItems();
Q_ASSERT(!qprojectM()->presetPositionValid());
bool presetExistsWithinFilter = false;
if ( historyHash.contains ( filter ) )
{
const PlaylistItemVector & playlistItems = *historyHash.value ( filter );
for ( PlaylistItemVector::const_iterator pos = playlistItems.begin(); pos != playlistItems.end();++pos )
{
const PlaylistItemMetaData & data = playlistItemMetaDataHash[*pos];
playlistModel->appendRow ( data.url, data.name, data.rating, data.breedability);
if (activePresetId.hasValue() && data.id == activePresetId.value()) {
qprojectM()->selectPresetPosition(playlistModel->rowCount()-1);
presetExistsWithinFilter = true;
}
}
}
else
{
const PlaylistItemVector & playlistItems = *historyHash.value ( QString() );
PlaylistItemVector * playlistItems2 = new PlaylistItemVector();
for ( PlaylistItemVector::const_iterator pos = playlistItems.begin(); pos != playlistItems.end();++pos )
{
const PlaylistItemMetaData & data = playlistItemMetaDataHash[*pos];
if ( ( data.name ).contains ( filter, Qt::CaseInsensitive ) )
{
playlistModel->appendRow ( data.url, data.name, data.rating, data.breedability);
if (activePresetId.hasValue() && data.id == activePresetId.value()) {
qprojectM()->selectPresetPosition(playlistModel->rowCount()-1);
presetExistsWithinFilter = true;
}
playlistItems2->push_back ( *pos );
}
}
historyHash.insert ( filter, playlistItems2 );
}
Q_ASSERT(presetExistsWithinFilter == qprojectM()->presetPositionValid());
previousFilter = filter;
qprojectMWidget()->releasePresetLock();
}
void QProjectM_MainWindow::presetRatingChanged( unsigned int index, int rating, PresetRatingType ratingType)
{
PlaylistItemVector & lastCache = *historyHash[previousFilter];
const long id = lastCache[index];
qDebug() << "preset rating changed: " << index << " (rating = " << rating << ")";
playlistItemMetaDataHash[id].rating = rating;
ui->presetPlayListDockWidget->setWindowModified ( true );
playlistModel->notifyDataChanged(index);
}