[UI] Added playlist editing features

This commit is contained in:
emeric
2014-12-01 12:55:55 +01:00
parent 215945f91e
commit 9af5ee25d7
16 changed files with 491 additions and 84 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ ui = {
enable = true; enable = true;
resources = { resources = {
docroot = "/usr/share/Wt/" docroot = "/var/lms/docroot"
approot = "/var/lms/approot" approot = "/var/lms/approot"
} }
+38
View File
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2014 Emeric Poupon
*
* This file is part of LMS.
*
* LMS 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 3 of the License, or
* (at your option) any later version.
*
* LMS 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AudioTypes.hpp"
namespace Database {
Playlist::Playlist()
: _public(false)
{
}
Playlist::Playlist(std::string name, Wt::Dbo::ptr<User> user, bool isPublic)
: _public(isPublic),
_user(user)
{
}
} // namespace Database
+39
View File
@@ -0,0 +1,39 @@
/*
* Copyright (C) 2014 Emeric Poupon
*
* This file is part of LMS.
*
* LMS 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 3 of the License, or
* (at your option) any later version.
*
* LMS 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AudioTypes.hpp"
namespace Database {
PlaylistEntry::PlaylistEntry()
: _position(0)
{
}
PlaylistEntry::PlaylistEntry(Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<Playlist> playlist)
: _position(0),
_track(track),
_playlist(playlist)
{
}
} // namespace Database
+2
View File
@@ -76,6 +76,8 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env, boost::filesystem::p
bootstrapTheme->setResponsive(true); bootstrapTheme->setResponsive(true);
setTheme(bootstrapTheme); setTheme(bootstrapTheme);
useStyleSheet("css/lms.css");
// Add a resource bundle // Add a resource bundle
messageResourceBundle().use(appRoot() + "templates"); messageResourceBundle().use(appRoot() + "templates");
+120 -66
View File
@@ -24,14 +24,13 @@
#include <Wt/WGridLayout> #include <Wt/WGridLayout>
#include <Wt/WComboBox> #include <Wt/WComboBox>
#include <Wt/WPushButton> #include <Wt/WPushButton>
#include <Wt/WPopupMenu>
#include "logger/Logger.hpp" #include "logger/Logger.hpp"
#include "TableFilter.hpp" #include "TableFilter.hpp"
#include "TableFilterGenre.hpp" #include "TableFilterGenre.hpp"
#include "KeywordSearchFilter.hpp" #include "KeywordSearchFilter.hpp"
#include "TrackView.hpp"
#include "PlayQueue.hpp"
#include "Audio.hpp" #include "Audio.hpp"
@@ -40,41 +39,37 @@ namespace UserInterface {
Audio::Audio(SessionData& sessionData, Wt::WContainerWidget* parent) Audio::Audio(SessionData& sessionData, Wt::WContainerWidget* parent)
: Wt::WContainerWidget(parent), : Wt::WContainerWidget(parent),
_db(sessionData.getDatabaseHandler()), _db(sessionData.getDatabaseHandler()),
_mediaPlayer(nullptr) _mediaPlayer(nullptr),
_trackView(nullptr),
_playQueue(nullptr)
{ {
Wt::WGridLayout *mainLayout = new Wt::WGridLayout(); Wt::WGridLayout *mainLayout = new Wt::WGridLayout();
this->setLayout(mainLayout); this->setLayout(mainLayout);
// Filters // Filters
Wt::WHBoxLayout *filterLayout = new Wt::WHBoxLayout(); Wt::WHBoxLayout *filterLayout = new Wt::WHBoxLayout();
{ TableFilterGenre *filterGenre = new TableFilterGenre(_db);
TableFilterGenre *filter = new TableFilterGenre(_db); filterLayout->addWidget(filterGenre);
filterLayout->addWidget(filter); _filterChain.addFilter(filterGenre);
_filterChain.addFilter(filter);
} TableFilter *filterArtist = new TableFilter(_db, "track", "artist_name", "Artist");
{ filterLayout->addWidget(filterArtist);
TableFilter *filter = new TableFilter(_db, "track", "artist_name"); _filterChain.addFilter(filterArtist);
filterLayout->addWidget(filter);
_filterChain.addFilter(filter); TableFilter *filterRelease = new TableFilter(_db, "track", "release_name", "Release");
} filterLayout->addWidget(filterRelease);
{ _filterChain.addFilter(filterRelease);
TableFilter *filter = new TableFilter(_db, "track", "release_name");
filterLayout->addWidget(filter);
_filterChain.addFilter(filter);
}
mainLayout->addLayout(filterLayout, 0, 0); mainLayout->addLayout(filterLayout, 0, 0);
// ENDOF(Filters) // ENDOF(Filters)
// TODO ADD controls (Play, add)
// TODO ADD some stats (nb files, total duration, etc.) // TODO ADD some stats (nb files, total duration, etc.)
Wt::WVBoxLayout* trackLayout = new Wt::WVBoxLayout(); Wt::WVBoxLayout* trackLayout = new Wt::WVBoxLayout();
TrackView* trackView = new TrackView(_db); _trackView = new TrackView(_db);
trackLayout->addWidget(trackView, 1); trackLayout->addWidget(_trackView, 1);
Wt::WHBoxLayout* trackControls = new Wt::WHBoxLayout(); Wt::WHBoxLayout* trackControls = new Wt::WHBoxLayout();
@@ -90,9 +85,9 @@ _mediaPlayer(nullptr)
mainLayout->addLayout(trackLayout, 1, 0); mainLayout->addLayout(trackLayout, 1, 0);
_filterChain.addFilter(trackView); _filterChain.addFilter(_trackView);
PlayQueue *playQueue = new PlayQueue(_db); _playQueue = new PlayQueue(_db);
// Playlist/PlayQueue // Playlist/PlayQueue
{ {
@@ -103,12 +98,49 @@ _mediaPlayer(nullptr)
Wt::WHBoxLayout* playlistControls = new Wt::WHBoxLayout(); Wt::WHBoxLayout* playlistControls = new Wt::WHBoxLayout();
playlistControls->addWidget(new Wt::WPushButton("Playlist")); Wt::WPushButton *saveBtn = new Wt::WPushButton("Save");
playlistControls->addWidget(new Wt::WText("Duration:")); playlistControls->addWidget(saveBtn);
Wt::WPushButton *loadBtn = new Wt::WPushButton("Load");
playlistControls->addWidget(loadBtn);
Wt::WPushButton *upBtn = new Wt::WPushButton("UP");
playlistControls->addWidget(upBtn);
Wt::WPushButton *downBtn = new Wt::WPushButton("DO");
playlistControls->addWidget(downBtn);
Wt::WPushButton *delBtn = new Wt::WPushButton("DEL");
playlistControls->addWidget(delBtn);
delBtn->clicked().connect(_playQueue, &PlayQueue::delSelected);
upBtn->clicked().connect(_playQueue, &PlayQueue::moveSelectedUp);
downBtn->clicked().connect(_playQueue, &PlayQueue::moveSelectedDown);
playlistControls->addWidget(new Wt::WText("Duration:"), 1);
// Load menu
{
Wt::WPopupMenu *popup = new Wt::WPopupMenu();
popup->addItem("Metal");
popup->addItem("Test");
loadBtn->setMenu(popup);
}
// Save Menu
{
Wt::WPopupMenu *popup = new Wt::WPopupMenu();
popup->addItem("New");
popup->addSeparator();
popup->addItem("Metal");
popup->addItem("Test");
saveBtn->setMenu(popup);
}
playQueueLayout->addLayout(playlistControls); playQueueLayout->addLayout(playlistControls);
playQueueLayout->addWidget( playQueue, 1); playQueueLayout->addWidget( _playQueue, 1);
mainLayout->addLayout(playQueueLayout, 0, 1, 2, 1); mainLayout->addLayout(playQueueLayout, 0, 1, 2, 1);
} }
@@ -122,54 +154,27 @@ _mediaPlayer(nullptr)
// Double click on track // Double click on track
// Set the selected tracks to the play queue // Set the selected tracks to the play queue
trackView->trackDoubleClicked().connect(std::bind([=] () _trackView->trackDoubleClicked().connect(boost::bind(&Audio::playSelectedTracks, this, PlayQueueAddSelectedTracks));
{
std::vector<Database::Track::id_type> trackIds;
trackView->getSelectedTracks(trackIds);
playQueue->clear(); // Double click on artist
playQueue->addTracks(trackIds); // Set the selected tracks to the play queue
filterArtist->sigDoubleClicked().connect(boost::bind(&Audio::playSelectedTracks, this, PlayQueueAddAllTracks));
playQueue->play(); filterRelease->sigDoubleClicked().connect(boost::bind(&Audio::playSelectedTracks, this, PlayQueueAddAllTracks));
filterGenre->sigDoubleClicked().connect(boost::bind(&Audio::playSelectedTracks, this, PlayQueueAddAllTracks));
}));
// Play button // Play button
// Set the selected tracks to the play queue // Set the selected tracks to the play queue
playBtn->clicked().connect(std::bind([=] () playBtn->clicked().connect(boost::bind(&Audio::playSelectedTracks, this, PlayQueueAddSelectedTracks));
{
std::vector<Database::Track::id_type> trackIds;
trackView->getSelectedTracks(trackIds);
// If nothing is selected, get the whole track list
if (trackIds.empty())
trackView->getTracks(trackIds);
playQueue->clear();
playQueue->addTracks(trackIds);
playQueue->play();
}));
// Add Button // Add Button
// Add the selected tracks at the end of the play queue // Add the selected tracks at the end of the play queue
addBtn->clicked().connect(std::bind([=] () addBtn->clicked().connect(this, &Audio::addSelectedTracks);
{
std::vector<Database::Track::id_type> trackIds;
trackView->getSelectedTracks(trackIds);
// If nothing is selected, get the whole track list _playQueue->playTrack().connect(this, &Audio::playTrack);
if (trackIds.empty())
trackView->getTracks(trackIds);
playQueue->addTracks(trackIds); _mediaPlayer->playbackEnded().connect(_playQueue, &PlayQueue::handlePlaybackComplete);
})); _mediaPlayer->playNext().connect(_playQueue, &PlayQueue::playNext);
_mediaPlayer->playPrevious().connect(_playQueue, &PlayQueue::playPrevious);
playQueue->playTrack().connect(this, &Audio::playTrack);
_mediaPlayer->playbackEnded().connect(playQueue, &PlayQueue::handlePlaybackComplete);
_mediaPlayer->playNext().connect(playQueue, &PlayQueue::playNext);
_mediaPlayer->playPrevious().connect(playQueue, &PlayQueue::playPrevious);
} }
void void
@@ -178,6 +183,55 @@ Audio::search(const std::string& searchText)
_filterChain.searchKeyword(searchText); _filterChain.searchKeyword(searchText);
} }
void
Audio::playSelectedTracks(PlayQueueAddType addType)
{
int firstTrackPos = 0;
std::vector<Database::Track::id_type> trackIds;
switch(addType)
{
case PlayQueueAddAllTracks:
// Play all the tracks, from the first one
firstTrackPos = 0;
_trackView->getTracks(trackIds);
break;
case PlayQueueAddSelectedTracks:
// If nothing or only ONE selected, get them all
if (_trackView->getNbSelectedTracks() <= 1)
{
// Start to play at the selected track, if any
firstTrackPos = _trackView->getFirstSelectedTrackPosition();
_trackView->getTracks(trackIds);
}
else
{
// Play all the selected tracks, frm the first one
firstTrackPos = 0;
_trackView->getSelectedTracks(trackIds);
}
break;
}
_playQueue->clear();
_playQueue->addTracks(trackIds);
_playQueue->play(firstTrackPos);
}
void
Audio::addSelectedTracks(void)
{
std::vector<Database::Track::id_type> trackIds;
_trackView->getSelectedTracks(trackIds);
// If nothing is selected, get the whole track list
if (trackIds.empty())
_trackView->getTracks(trackIds);
_playQueue->addTracks(trackIds);
}
void void
Audio::playTrack(boost::filesystem::path p) Audio::playTrack(boost::filesystem::path p)
{ {
+13
View File
@@ -27,6 +27,8 @@
#include "common/SessionData.hpp" #include "common/SessionData.hpp"
#include "AudioMediaPlayer.hpp" #include "AudioMediaPlayer.hpp"
#include "TrackView.hpp"
#include "PlayQueue.hpp"
#include "FilterChain.hpp" #include "FilterChain.hpp"
@@ -45,11 +47,22 @@ class Audio : public Wt::WContainerWidget
void playTrack(boost::filesystem::path p); void playTrack(boost::filesystem::path p);
enum PlayQueueAddType
{
PlayQueueAddAllTracks,
PlayQueueAddSelectedTracks,
};
void playSelectedTracks(PlayQueueAddType addType);
void addSelectedTracks();
void handlePlaylistSelected(Wt::WString name); void handlePlaylistSelected(Wt::WString name);
Database::Handler& _db; Database::Handler& _db;
AudioMediaPlayer* _mediaPlayer; AudioMediaPlayer* _mediaPlayer;
TrackView* _trackView;
PlayQueue* _playQueue;
FilterChain _filterChain; FilterChain _filterChain;
+2
View File
@@ -63,11 +63,13 @@ AudioMediaPlayer::AudioMediaPlayer( Wt::WContainerWidget *parent)
nextBtn->clicked().connect(std::bind([=] () nextBtn->clicked().connect(std::bind([=] ()
{ {
_mediaPlayer->stop();
_playNext.emit(); _playNext.emit();
})); }));
prevBtn->clicked().connect(std::bind([=] () prevBtn->clicked().connect(std::bind([=] ()
{ {
_mediaPlayer->stop();
_playPrevious.emit(); _playPrevious.emit();
})); }));
} }
+186 -10
View File
@@ -17,15 +17,64 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Wt/WItemDelegate>
#include "logger/Logger.hpp"
#include "PlayQueue.hpp" #include "PlayQueue.hpp"
namespace {
void modelForceRefreshDataRow(Wt::WStandardItemModel *model, int row)
{
for (int i = 0; i < model->columnCount(); ++i)
model->setData(row, i, model->data(row, i));
}
void swapRows(Wt::WStandardItemModel *model, int row1, int row2)
{
// Copy column by column
for (int i = 0; i < model->columnCount(); ++i)
{
boost::any tmp = model->data(row1, i); // 1 -> tmp
model->setData(row1, i, model->data(row2, i)); // 2 -> 1
model->setData(row2, i, tmp); // tmp-> 2
}
}
}
namespace UserInterface { namespace UserInterface {
static const int trackIdInvalid = -1;
class PlayQueueItemDelegate : public Wt::WItemDelegate
{
public:
PlayQueueItemDelegate(Wt::WObject *parent = 0) : Wt::WItemDelegate(parent), _selectedRowId(trackIdInvalid) {}
void setSelectedRowId(int rowId) { _selectedRowId = rowId; }
Wt::WWidget* update(Wt::WWidget *widget, const Wt::WModelIndex &index, Wt::WFlags< Wt::ViewItemRenderFlag > flags)
{
Wt::WWidget* res = Wt::WItemDelegate::update(widget, index, flags);
if (res && index.isValid() && index.row() == _selectedRowId)
res->toggleStyleClass("playqueue-playing", true);
return res;
}
private:
int _selectedRowId;
};
PlayQueue::PlayQueue(Database::Handler& db, Wt::WContainerWidget* parent) PlayQueue::PlayQueue(Database::Handler& db, Wt::WContainerWidget* parent)
: Wt::WTableView(parent), : Wt::WTableView(parent),
_db(db), _db(db),
_playedId(-1) _playedId(trackIdInvalid)
{ {
_model = new Wt::WStandardItemModel(0, 4, this); _model = new Wt::WStandardItemModel(0, 4, this);
@@ -45,6 +94,9 @@ _playedId(-1)
this->setColumnHidden(0, true); this->setColumnHidden(0, true);
_itemDelegate = new PlayQueueItemDelegate();
this->setItemDelegate(_itemDelegate);
this->doubleClicked().connect( std::bind([=] (Wt::WModelIndex idx, Wt::WMouseEvent evt) this->doubleClicked().connect( std::bind([=] (Wt::WModelIndex idx, Wt::WMouseEvent evt)
{ {
if (!idx.isValid()) if (!idx.isValid())
@@ -64,9 +116,9 @@ _playedId(-1)
} }
void void
PlayQueue::play(void) PlayQueue::play(int trackId)
{ {
readTrack(0); readTrack(trackId);
} }
void void
@@ -99,23 +151,21 @@ PlayQueue::clear(void)
_model->removeRows(0, _model->rowCount()); _model->removeRows(0, _model->rowCount());
// Reset play id // Reset play id
_playedId = -1; _playedId = trackIdInvalid;
} }
void void
PlayQueue::handlePlaybackComplete(void) PlayQueue::handlePlaybackComplete(void)
{ {
// Play the next track // Play the next track
if (_playedId != -1) if (_playedId != trackIdInvalid)
{
readTrack(_playedId + 1); readTrack(_playedId + 1);
}
} }
void void
PlayQueue::playNext(void) PlayQueue::playNext(void)
{ {
readTrack( _playedId != -1 ? _playedId + 1 : 0); readTrack( _playedId != trackIdInvalid ? _playedId + 1 : 0);
} }
void void
@@ -136,15 +186,141 @@ PlayQueue::readTrack(int rowId)
Database::Track::pointer track = Database::Track::getById(_db.getSession(), trackId); Database::Track::pointer track = Database::Track::getById(_db.getSession(), trackId);
if (track) if (track)
{ {
_playedId = rowId; setPlayingTrackId(rowId);
_sigTrackPlay.emit(track->getPath()); _sigTrackPlay.emit(track->getPath());
break;
return;
} }
++rowId; ++rowId;
} }
} }
void
PlayQueue::setPlayingTrackId(int newRowId)
{
int oldRowId = _playedId;
_playedId = newRowId;
_itemDelegate->setSelectedRowId(newRowId);
// Hack re-set the data in order to trigger the rerending of the widget
// calling the update method of our custom item delegate gives bad results
if (oldRowId >= 0)
modelForceRefreshDataRow(_model, oldRowId);
if (newRowId >= 0)
modelForceRefreshDataRow(_model, newRowId);
}
void
PlayQueue::delSelected(void)
{
int minId = _model->rowCount();
Wt::WModelIndexSet indexSet = this->selectedIndexes();
BOOST_REVERSE_FOREACH(Wt::WModelIndex index, indexSet)
{
_model->removeRow(index.row());
if (index.row() < minId)
minId = index.row();
}
// If the current played track is removed, make sure to unselect it
_itemDelegate->setSelectedRowId(trackIdInvalid);
renumber(minId, _model->rowCount() - 1);
}
void
PlayQueue::moveSelectedUp(void)
{
int minId = _model->rowCount();
int maxId = 0;
Wt::WModelIndexSet indexSet = this->selectedIndexes();
Wt::WModelIndexSet newIndexSet;
// ordered from up to down
BOOST_FOREACH(Wt::WModelIndex index, indexSet)
{
// Do nothing if the first selected index is on the top
if (index.row() == 0)
return;
// TODO optimize for blocks
swapRows(_model, index.row() - 1, index.row());
if (index.row() - 1 < minId)
minId = index.row() - 1;
if (index.row() > maxId)
maxId = index.row();
// Update playing status
if (_playedId == index.row())
setPlayingTrackId(_playedId - 1);
else if (_playedId == index.row() - 1)
setPlayingTrackId(_playedId + 1);
newIndexSet.insert( _model->index(index.row() - 1, 0));
}
this->setSelectedIndexes( newIndexSet );
renumber(minId, maxId);
}
void
PlayQueue::moveSelectedDown(void)
{
int minId = _model->rowCount();
int maxId = 0;
Wt::WModelIndexSet indexSet = this->selectedIndexes();
Wt::WModelIndexSet newIndexSet;
// ordered from up to down
BOOST_REVERSE_FOREACH(Wt::WModelIndex index, indexSet)
{
// Do nothing if the last selected index is the last one
if (index.row() == _model->rowCount() - 1)
return;
// TODO optimize for blocks
swapRows(_model, index.row(), index.row() + 1);
if (index.row() < minId)
minId = index.row();
if (index.row() + 1 > maxId)
maxId = index.row() + 1;
// Update playing status
if (_playedId == index.row())
setPlayingTrackId(_playedId + 1);
else if (_playedId == index.row() + 1)
setPlayingTrackId(_playedId - 1);
newIndexSet.insert( _model->index(index.row() + 1, 0));
}
this->setSelectedIndexes( newIndexSet );
renumber(minId, maxId);
}
void
PlayQueue::renumber(int firstId, int lastId)
{
for (int i = firstId; i <= lastId; ++i)
_model->setData( i, 1, i + 1);
}
} // namespace UserInterface } // namespace UserInterface
+13 -1
View File
@@ -28,6 +28,8 @@
namespace UserInterface { namespace UserInterface {
class PlayQueueItemDelegate;
class PlayQueue : public Wt::WTableView class PlayQueue : public Wt::WTableView
{ {
public: public:
@@ -37,10 +39,16 @@ class PlayQueue : public Wt::WTableView
void clear(void); void clear(void);
void play(void); // Play the queue from the beginning // Play functions
void play(int trackId = 0); // Play the queue
void playNext(void); // Play the next track void playNext(void); // Play the next track
void playPrevious(void); // Play the previous track void playPrevious(void); // Play the previous track
// List manipulations
void delSelected(void);
void moveSelectedUp(void);
void moveSelectedDown(void);
// Signals // Signals
Wt::Signal< boost::filesystem::path >& playTrack() { return _sigTrackPlay; } Wt::Signal< boost::filesystem::path >& playTrack() { return _sigTrackPlay; }
@@ -52,6 +60,8 @@ class PlayQueue : public Wt::WTableView
private: private:
void readTrack(int rowId); void readTrack(int rowId);
void setPlayingTrackId(int newRowId);
void renumber(int firstId, int lastId);
Wt::Signal< boost::filesystem::path > _sigTrackPlay; Wt::Signal< boost::filesystem::path > _sigTrackPlay;
@@ -59,6 +69,8 @@ class PlayQueue : public Wt::WTableView
Wt::WStandardItemModel* _model; Wt::WStandardItemModel* _model;
PlayQueueItemDelegate* _itemDelegate;
int _playedId; int _playedId;
}; };
+18 -3
View File
@@ -25,7 +25,7 @@
namespace UserInterface { namespace UserInterface {
TableFilter::TableFilter(Database::Handler& db, std::string table, std::string field, Wt::WContainerWidget* parent) TableFilter::TableFilter(Database::Handler& db, std::string table, std::string field, const Wt::WString& displayName, Wt::WContainerWidget* parent)
: Wt::WTableView( parent ), : Wt::WTableView( parent ),
Filter(), Filter(),
_db(db), _db(db),
@@ -33,8 +33,8 @@ _table(table),
_field(field) _field(field)
{ {
_queryModel.setQuery( _db.getSession().query< ResultType >("select track." + _field + ", COUNT(DISTINCT track.id) from track GROUP BY track." + _field).orderBy("track." + _field)); _queryModel.setQuery( _db.getSession().query< ResultType >("select track." + _field + ", COUNT(DISTINCT track.id) from track GROUP BY track." + _field).orderBy("track." + _field));
_queryModel.addColumn( "track." + _field, field); _queryModel.addColumn( "track." + _field, displayName);
_queryModel.addColumn( "COUNT(DISTINCT track.id)", "tracks"); _queryModel.addColumn( "COUNT(DISTINCT track.id)", "Tracks");
this->setSelectionMode(Wt::ExtendedSelection); this->setSelectionMode(Wt::ExtendedSelection);
this->setSortingEnabled(false); this->setSortingEnabled(false);
@@ -46,6 +46,21 @@ _field(field)
setLayoutSizeAware(true); setLayoutSizeAware(true);
_queryModel.setBatchSize(100); _queryModel.setBatchSize(100);
// If an item is double clicked, select and emit signal
this->doubleClicked().connect( std::bind([=] (Wt::WModelIndex idx, Wt::WMouseEvent evt)
{
if (!idx.isValid())
return;
Wt::WModelIndexSet indexSet;
indexSet.insert(idx);
this->setSelectedIndexes( indexSet );
_sigDoubleClicked.emit( );
}, std::placeholders::_1, std::placeholders::_2));
} }
void void
+7 -1
View File
@@ -33,7 +33,7 @@ class TableFilter : public Wt::WTableView, public Filter
{ {
public: public:
TableFilter(Database::Handler& db, std::string table, std::string field, Wt::WContainerWidget* parent = 0); TableFilter(Database::Handler& db, std::string table, std::string field, const Wt::WString& displayName, Wt::WContainerWidget* parent = 0);
// Set constraints on this filter // Set constraints on this filter
void refresh(const Constraint& constraint); void refresh(const Constraint& constraint);
@@ -43,8 +43,14 @@ class TableFilter : public Wt::WTableView, public Filter
void layoutSizeChanged (int width, int height); void layoutSizeChanged (int width, int height);
typedef Wt::Signal<void> SigDoubleClicked;
SigDoubleClicked& sigDoubleClicked() { return _sigDoubleClicked; }
protected: protected:
SigDoubleClicked _sigDoubleClicked;
Database::Handler& _db; Database::Handler& _db;
const std::string _table; const std::string _table;
const std::string _field; const std::string _field;
+14
View File
@@ -44,6 +44,20 @@ _db(db)
setLayoutSizeAware(true); setLayoutSizeAware(true);
_queryModel.setBatchSize(100); _queryModel.setBatchSize(100);
// If an item is double clicked, select and emit signal
this->doubleClicked().connect( std::bind([=] (Wt::WModelIndex idx, Wt::WMouseEvent evt)
{
if (!idx.isValid())
return;
Wt::WModelIndexSet indexSet;
indexSet.insert(idx);
this->setSelectedIndexes( indexSet );
_sigDoubleClicked.emit( );
}, std::placeholders::_1, std::placeholders::_2));
} }
void void
+6
View File
@@ -43,8 +43,14 @@ class TableFilterGenre : public Wt::WTableView, public Filter
void layoutSizeChanged (int width, int height); void layoutSizeChanged (int width, int height);
typedef Wt::Signal<void> SigDoubleClicked;
SigDoubleClicked& sigDoubleClicked() { return _sigDoubleClicked; }
protected: protected:
SigDoubleClicked _sigDoubleClicked;
Database::Handler& _db; Database::Handler& _db;
// genre_name, count // genre_name, count
+21
View File
@@ -149,6 +149,27 @@ TrackView::getSelectedTracks(std::vector<Database::Track::id_type>& track_ids)
LMS_LOG(MOD_UI, SEV_DEBUG) << "Getting all selected tracks DONE..."; LMS_LOG(MOD_UI, SEV_DEBUG) << "Getting all selected tracks DONE...";
} }
std::size_t
TrackView::getNbSelectedTracks(void)
{
return this->selectedIndexes().size();
}
int
TrackView::getFirstSelectedTrackPosition(void)
{
Wt::WModelIndexSet indexSet = this->selectedIndexes();
BOOST_FOREACH(Wt::WModelIndex index, indexSet)
{
if (!index.isValid())
continue;
return index.row();
}
return 0;
}
void void
TrackView::getTracks(std::vector<Database::Track::id_type>& trackIds) TrackView::getTracks(std::vector<Database::Track::id_type>& trackIds)
{ {
+6 -2
View File
@@ -43,9 +43,13 @@ class TrackView : public Wt::WTableView, public Filter
// Create constraints for child filters (N/A) // Create constraints for child filters (N/A)
void getConstraint(Constraint& constraint) {} void getConstraint(Constraint& constraint) {}
// Get all the tracks that are currently selected // Get all the tracks that are currently selected
void getSelectedTracks(std::vector<Database::Track::id_type>& track_ids); void getSelectedTracks(std::vector<Database::Track::id_type>& trackIds);
std::size_t getNbSelectedTracks(void);
// Get the first position of the selected tracks (0 if nothing selected)
int getFirstSelectedTrackPosition(void);
// Get all the tracks // Get all the tracks
void getTracks(std::vector<Database::Track::id_type>& track_ids); void getTracks(std::vector<Database::Track::id_type>& track_ids);
+5
View File
@@ -0,0 +1,5 @@
.playqueue-playing {
font-weight: bold;
}