WIP. new layout on the audio view
This commit is contained in:
+47
-47
@@ -1,64 +1,64 @@
|
||||
bin_PROGRAMS = lms
|
||||
|
||||
lms_SOURCES = \
|
||||
$(srcdir)/main/main.cpp \
|
||||
$(srcdir)/av/CodecContext.cpp \
|
||||
$(srcdir)/av/Common.cpp \
|
||||
$(srcdir)/av/Dictionary.cpp \
|
||||
$(srcdir)/av/FormatContext.cpp \
|
||||
$(srcdir)/av/InputFormatContext.cpp \
|
||||
$(srcdir)/av/Stream.cpp \
|
||||
$(srcdir)/config/ConfigReader.cpp \
|
||||
$(srcdir)/cover/CoverArt.cpp \
|
||||
$(srcdir)/cover/CoverArtGrabber.cpp \
|
||||
$(srcdir)/database/Artist.cpp \
|
||||
$(srcdir)/database/Genre.cpp \
|
||||
$(srcdir)/database/Release.cpp \
|
||||
$(srcdir)/database/MediaDirectory.cpp \
|
||||
$(srcdir)/database/Track.cpp \
|
||||
$(srcdir)/database/DatabaseHandler.cpp \
|
||||
$(srcdir)/database/SqlQuery.cpp \
|
||||
$(srcdir)/database/Video.cpp \
|
||||
$(srcdir)/database/User.cpp \
|
||||
$(srcdir)/database-updater/DatabaseUpdater.cpp \
|
||||
$(srcdir)/database-updater/Checksum.cpp \
|
||||
$(srcdir)/logger/Logger.cpp \
|
||||
$(srcdir)/metadata/AvFormat.cpp \
|
||||
$(srcdir)/metadata/Utils.cpp \
|
||||
$(srcdir)/remote/server/Connection.cpp \
|
||||
$(srcdir)/remote/server/ConnectionManager.cpp \
|
||||
$(srcdir)/main/main.cpp \
|
||||
$(srcdir)/av/CodecContext.cpp \
|
||||
$(srcdir)/av/Common.cpp \
|
||||
$(srcdir)/av/Dictionary.cpp \
|
||||
$(srcdir)/av/FormatContext.cpp \
|
||||
$(srcdir)/av/InputFormatContext.cpp \
|
||||
$(srcdir)/av/Stream.cpp \
|
||||
$(srcdir)/config/ConfigReader.cpp \
|
||||
$(srcdir)/cover/CoverArt.cpp \
|
||||
$(srcdir)/cover/CoverArtGrabber.cpp \
|
||||
$(srcdir)/database/Artist.cpp \
|
||||
$(srcdir)/database/Genre.cpp \
|
||||
$(srcdir)/database/Release.cpp \
|
||||
$(srcdir)/database/MediaDirectory.cpp \
|
||||
$(srcdir)/database/Track.cpp \
|
||||
$(srcdir)/database/DatabaseHandler.cpp \
|
||||
$(srcdir)/database/SqlQuery.cpp \
|
||||
$(srcdir)/database/Video.cpp \
|
||||
$(srcdir)/database/User.cpp \
|
||||
$(srcdir)/database-updater/DatabaseUpdater.cpp \
|
||||
$(srcdir)/database-updater/Checksum.cpp \
|
||||
$(srcdir)/logger/Logger.cpp \
|
||||
$(srcdir)/metadata/AvFormat.cpp \
|
||||
$(srcdir)/metadata/Utils.cpp \
|
||||
$(srcdir)/remote/server/Connection.cpp \
|
||||
$(srcdir)/remote/server/ConnectionManager.cpp \
|
||||
$(srcdir)/remote/server/AudioCollectionRequestHandler.cpp \
|
||||
$(srcdir)/remote/server/AuthRequestHandler.cpp \
|
||||
$(srcdir)/remote/server/MediaRequestHandler.cpp \
|
||||
$(srcdir)/remote/server/AuthRequestHandler.cpp \
|
||||
$(srcdir)/remote/server/MediaRequestHandler.cpp \
|
||||
$(srcdir)/remote/server/RequestHandler.cpp \
|
||||
$(srcdir)/remote/server/Server.cpp \
|
||||
$(srcdir)/service/ServiceManager.cpp \
|
||||
$(srcdir)/service/DatabaseUpdateService.cpp \
|
||||
$(srcdir)/remote/server/Server.cpp \
|
||||
$(srcdir)/service/ServiceManager.cpp \
|
||||
$(srcdir)/service/DatabaseUpdateService.cpp \
|
||||
$(srcdir)/service/UserInterfaceService.cpp \
|
||||
$(srcdir)/service/RemoteServerService.cpp \
|
||||
$(srcdir)/transcode/AvConvTranscoder.cpp \
|
||||
$(srcdir)/transcode/Format.cpp \
|
||||
$(srcdir)/transcode/Parameters.cpp \
|
||||
$(srcdir)/transcode/InputMediaFile.cpp \
|
||||
$(srcdir)/ui/LmsApplication.cpp \
|
||||
$(srcdir)/ui/LmsHome.cpp \
|
||||
$(srcdir)/ui/auth/LmsAuth.cpp \
|
||||
$(srcdir)/service/RemoteServerService.cpp \
|
||||
$(srcdir)/transcode/AvConvTranscoder.cpp \
|
||||
$(srcdir)/transcode/Format.cpp \
|
||||
$(srcdir)/transcode/Parameters.cpp \
|
||||
$(srcdir)/transcode/InputMediaFile.cpp \
|
||||
$(srcdir)/ui/LmsApplication.cpp \
|
||||
$(srcdir)/ui/LmsHome.cpp \
|
||||
$(srcdir)/ui/auth/LmsAuth.cpp \
|
||||
$(srcdir)/ui/audio/AudioWidget.cpp \
|
||||
$(srcdir)/ui/audio/AudioDatabaseWidget.cpp \
|
||||
$(srcdir)/ui/audio/AudioMediaPlayerWidget.cpp \
|
||||
$(srcdir)/ui/audio/SearchFilterWidget.cpp \
|
||||
$(srcdir)/ui/audio/TableFilterWidget.cpp \
|
||||
$(srcdir)/ui/audio/TrackWidget.cpp \
|
||||
$(srcdir)/ui/audio/AudioMediaPlayerWidget.cpp \
|
||||
$(srcdir)/ui/audio/FilterChain.cpp \
|
||||
$(srcdir)/ui/audio/KeywordSearchFilter.cpp \
|
||||
$(srcdir)/ui/audio/TableFilter.cpp \
|
||||
$(srcdir)/ui/audio/TrackView.cpp \
|
||||
$(srcdir)/ui/common/DirectoryValidator.cpp \
|
||||
$(srcdir)/ui/common/SessionData.cpp \
|
||||
$(srcdir)/ui/resource/AvConvTranscodeStreamResource.cpp \
|
||||
$(srcdir)/ui/video/VideoWidget.cpp \
|
||||
$(srcdir)/ui/video/VideoDatabaseWidget.cpp \
|
||||
$(srcdir)/ui/video/VideoMediaPlayerWidget.cpp \
|
||||
$(srcdir)/ui/video/VideoParametersDialog.cpp \
|
||||
$(srcdir)/ui/video/VideoMediaPlayerWidget.cpp \
|
||||
$(srcdir)/ui/video/VideoParametersDialog.cpp \
|
||||
$(srcdir)/ui/settings/Settings.cpp \
|
||||
$(srcdir)/ui/settings/SettingsAccountFormView.cpp \
|
||||
$(srcdir)/ui/settings/SettingsAudioFormView.cpp \
|
||||
$(srcdir)/ui/settings/SettingsAudioFormView.cpp \
|
||||
$(srcdir)/ui/settings/SettingsDatabaseFormView.cpp \
|
||||
$(srcdir)/ui/settings/SettingsFirstConnectionFormView.cpp \
|
||||
$(srcdir)/ui/settings/SettingsMediaDirectories.cpp \
|
||||
|
||||
@@ -70,6 +70,7 @@ AuthRequestHandler::processPassword(const AuthRequest::Password& request, AuthRe
|
||||
{
|
||||
case Wt::Auth::PasswordInvalid:
|
||||
response.set_type(AuthResponse::PasswordResult::TypePasswordInvalid);
|
||||
LMS_LOG(MOD_REMOTE, SEV_NOTICE) << "User '" << request.user_login() << "': invalid password";
|
||||
res = true;
|
||||
break;
|
||||
case Wt::Auth::LoginThrottling:
|
||||
@@ -81,9 +82,11 @@ AuthRequestHandler::processPassword(const AuthRequest::Password& request, AuthRe
|
||||
response.set_type(AuthResponse::PasswordResult::TypePasswordValid);
|
||||
// Log the user in
|
||||
_db.getLogin().login( user );
|
||||
LMS_LOG(MOD_REMOTE, SEV_NOTICE) << "User '" << request.user_login() << "' successfully logged in";
|
||||
res = true;
|
||||
break;
|
||||
default:
|
||||
LMS_LOG(MOD_REMOTE, SEV_ERROR) << "Cannot handle password result!";
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+50
-47
@@ -18,8 +18,24 @@
|
||||
*/
|
||||
|
||||
#include <Wt/WBootstrapTheme>
|
||||
#include <Wt/WVBoxLayout>
|
||||
#include <Wt/WHBoxLayout>
|
||||
#include <Wt/WNavigationBar>
|
||||
#include <Wt/WStackedWidget>
|
||||
#include <Wt/WMenu>
|
||||
#include <Wt/WNavigationBar>
|
||||
#include <Wt/WPopupMenu>
|
||||
#include <Wt/WPopupMenuItem>
|
||||
#include <Wt/WVBoxLayout>
|
||||
#include <Wt/Auth/Identity>
|
||||
|
||||
#include "settings/Settings.hpp"
|
||||
|
||||
|
||||
#include "auth/LmsAuth.hpp"
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
|
||||
#include "LmsHome.hpp"
|
||||
#include "settings/SettingsFirstConnectionFormView.hpp"
|
||||
|
||||
@@ -64,65 +80,52 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env, boost::filesystem::p
|
||||
|
||||
setTitle("LMS"); // application title
|
||||
|
||||
bool firstConnection;
|
||||
{
|
||||
Wt::Dbo::Transaction transaction(_sessionData.getDatabaseHandler().getSession());
|
||||
// Create a Vertical layout: top is the nav bar, bottom is the contents
|
||||
Wt::WVBoxLayout *layout = new Wt::WVBoxLayout(this->root());
|
||||
// Create a navigation bar with a link to a web page.
|
||||
Wt::WNavigationBar *navigation = new Wt::WNavigationBar();
|
||||
navigation->setTitle("LMS");
|
||||
navigation->setResponsive(true);
|
||||
navigation->addStyleClass("main-nav");
|
||||
|
||||
firstConnection = (Database::User::getAll(_sessionData.getDatabaseHandler().getSession()).size() == 0);
|
||||
}
|
||||
Wt::WStackedWidget *contentsStack = new Wt::WStackedWidget();
|
||||
|
||||
// If here is no account in the database, launch the first connection wizard
|
||||
if (firstConnection)
|
||||
{
|
||||
// Hack, use the auth widget builtin strings
|
||||
builtinLocalizedStrings().useBuiltin(skeletons::AuthStrings_xml1);
|
||||
// Setup a Left-aligned menu.
|
||||
Wt::WMenu *leftMenu = new Wt::WMenu(contentsStack);
|
||||
navigation->addMenu(leftMenu);
|
||||
|
||||
root()->addWidget( new Settings::FirstConnectionFormView(_sessionData));
|
||||
}
|
||||
else
|
||||
{
|
||||
_sessionData.getDatabaseHandler().getLogin().changed().connect(this, &LmsApplication::handleAuthEvent);
|
||||
_audioWidget = new AudioWidget(_sessionData);
|
||||
_videoWidget = new VideoWidget(_sessionData);
|
||||
|
||||
LmsAuth *authWidget = new LmsAuth(_sessionData.getDatabaseHandler());
|
||||
leftMenu->addItem("Audio", _audioWidget);
|
||||
leftMenu->addItem("Video", _videoWidget);
|
||||
// leftMenu->addItem("Settings", new Settings::Settings(_sessionData));
|
||||
|
||||
authWidget->model()->addPasswordAuth(&Database::Handler::getPasswordService());
|
||||
authWidget->setRegistrationEnabled(false);
|
||||
// Setup a Right-aligned menu.
|
||||
Wt::WMenu *rightMenu = new Wt::WMenu();
|
||||
|
||||
authWidget->processEnvironment();
|
||||
navigation->addMenu(rightMenu, Wt::AlignRight);
|
||||
|
||||
root()->addWidget(authWidget);
|
||||
}
|
||||
}
|
||||
Wt::WPopupMenu *popup = new Wt::WPopupMenu();
|
||||
popup->addItem("Logout");
|
||||
|
||||
// popup->itemSelected().connect(this, &LmsHome::handleUserMenuSelected);
|
||||
|
||||
/* Wt::WMenuItem *item = new Wt::WMenuItem( user.identity(Wt::Auth::Identity::LoginName) );
|
||||
item->setMenu(popup);
|
||||
rightMenu->addItem(item);*/
|
||||
|
||||
void
|
||||
LmsApplication::handleAuthEvent(void)
|
||||
{
|
||||
if (_sessionData.getDatabaseHandler().getLogin().loggedIn())
|
||||
{
|
||||
if (_home == nullptr) {
|
||||
_home = new LmsHome(_sessionData, root() );
|
||||
}
|
||||
else
|
||||
std::cerr << "Already logged in??" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "user log out" << std::endl;
|
||||
if (_home != nullptr) {
|
||||
delete _home;
|
||||
_home = nullptr;
|
||||
// Add a Search control.
|
||||
/* _searchEdit = new Wt::WLineEdit();
|
||||
_searchEdit->setEmptyText("Search...");
|
||||
|
||||
// Hack: quit/redirect in order to avoid 'signal not exposed' problems
|
||||
// TODO, investigate/remove?
|
||||
quit();
|
||||
redirect("/");
|
||||
_searchEdit->enterPressed().connect(this, &LmsHome::handleSearch);
|
||||
*/
|
||||
// navigation->addSearch(_searchEdit, Wt::AlignLeft);
|
||||
|
||||
}
|
||||
else
|
||||
std::cerr << "Already logged out??" << std::endl;
|
||||
}
|
||||
layout->addWidget(navigation);
|
||||
layout->addWidget(contentsStack, 1);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
@@ -44,6 +44,8 @@ class LmsApplication : public Wt::WApplication
|
||||
SessionData _sessionData;
|
||||
|
||||
LmsHome* _home;
|
||||
AudioWidget* _audioWidget;
|
||||
VideoWidget* _videoWidget;
|
||||
|
||||
};
|
||||
|
||||
|
||||
+1
-41
@@ -22,6 +22,7 @@
|
||||
#include <Wt/WNavigationBar>
|
||||
#include <Wt/WPopupMenu>
|
||||
#include <Wt/WPopupMenuItem>
|
||||
#include <Wt/WVBoxLayout>
|
||||
#include <Wt/Auth/Identity>
|
||||
|
||||
#include "settings/Settings.hpp"
|
||||
@@ -36,48 +37,7 @@ LmsHome::LmsHome(SessionData& sessionData, Wt::WContainerWidget* parent)
|
||||
{
|
||||
const Wt::Auth::User& user = sessionData.getDatabaseHandler().getLogin().user();
|
||||
|
||||
// Create a navigation bar with a link to a web page.
|
||||
Wt::WNavigationBar *navigation = new Wt::WNavigationBar(this);
|
||||
navigation->setTitle("LMS");
|
||||
navigation->setResponsive(true);
|
||||
navigation->addStyleClass("main-nav");
|
||||
|
||||
Wt::WStackedWidget *contentsStack = new Wt::WStackedWidget(this);
|
||||
|
||||
// Setup a Left-aligned menu.
|
||||
Wt::WMenu *leftMenu = new Wt::WMenu(contentsStack);
|
||||
navigation->addMenu(leftMenu);
|
||||
|
||||
_audioWidget = new AudioWidget(_sessionData);
|
||||
_videoWidget = new VideoWidget(_sessionData);
|
||||
|
||||
leftMenu->addItem("Audio", _audioWidget);
|
||||
leftMenu->addItem("Video", _videoWidget);
|
||||
leftMenu->addItem("Settings", new Settings::Settings(_sessionData));
|
||||
|
||||
// Setup a Right-aligned menu.
|
||||
Wt::WMenu *rightMenu = new Wt::WMenu();
|
||||
|
||||
navigation->addMenu(rightMenu, Wt::AlignRight);
|
||||
|
||||
Wt::WPopupMenu *popup = new Wt::WPopupMenu();
|
||||
popup->addItem("Logout");
|
||||
|
||||
popup->itemSelected().connect(this, &LmsHome::handleUserMenuSelected);
|
||||
|
||||
Wt::WMenuItem *item = new Wt::WMenuItem( user.identity(Wt::Auth::Identity::LoginName) );
|
||||
item->setMenu(popup);
|
||||
rightMenu->addItem(item);
|
||||
|
||||
// Add a Search control.
|
||||
_searchEdit = new Wt::WLineEdit();
|
||||
_searchEdit->setEmptyText("Search...");
|
||||
|
||||
_searchEdit->enterPressed().connect(this, &LmsHome::handleSearch);
|
||||
|
||||
navigation->addSearch(_searchEdit, Wt::AlignLeft);
|
||||
|
||||
addWidget(contentsStack);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 <boost/foreach.hpp>
|
||||
|
||||
#include <Wt/WTable> // TODO
|
||||
#include <Wt/WBreak> // TODO
|
||||
|
||||
#include "AudioDatabaseWidget.hpp"
|
||||
|
||||
#include "TableFilterWidget.hpp"
|
||||
#include "SearchFilterWidget.hpp"
|
||||
#include "TrackWidget.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
AudioDatabaseWidget::AudioDatabaseWidget( Database::Handler& db, Wt::WContainerWidget *parent)
|
||||
: Wt::WContainerWidget(parent),
|
||||
_refreshingFilters(false)
|
||||
{
|
||||
std::size_t idFilter (0);
|
||||
{
|
||||
SearchFilterWidget* search = new SearchFilterWidget(this);
|
||||
_filters.push_back( search );
|
||||
search->update().connect( boost::bind(&AudioDatabaseWidget::handleFilterUpdated, this, idFilter++) );
|
||||
}
|
||||
|
||||
Wt::WTable* table = new Wt::WTable(this);
|
||||
|
||||
{
|
||||
TableFilterWidget* filterTable = new TableFilterWidget(db, "genre", "name", table->elementAt(0,0));
|
||||
_filters.push_back( filterTable );
|
||||
filterTable->update().connect( boost::bind(&AudioDatabaseWidget::handleFilterUpdated, this, idFilter++) );
|
||||
}
|
||||
{
|
||||
TableFilterWidget* filterTable = new TableFilterWidget(db, "artist", "name", table->elementAt(0,1));
|
||||
_filters.push_back( filterTable );
|
||||
filterTable->update().connect( boost::bind(&AudioDatabaseWidget::handleFilterUpdated, this, idFilter++) );
|
||||
}
|
||||
{
|
||||
TableFilterWidget* filterTable = new TableFilterWidget(db, "release", "name", table->elementAt(0,2));
|
||||
_filters.push_back( filterTable );
|
||||
filterTable->update().connect( boost::bind(&AudioDatabaseWidget::handleFilterUpdated, this, idFilter++) );
|
||||
}
|
||||
|
||||
{
|
||||
TrackWidget* track = new TrackWidget(db, this);
|
||||
_filters.push_back( track );
|
||||
track->trackSelected().connect(this, &AudioDatabaseWidget::handleTrackSelected);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
AudioDatabaseWidget::search(const std::string& text)
|
||||
{
|
||||
SearchFilterWidget* searchWidget ( dynamic_cast<SearchFilterWidget*>(_filters.front() ) );
|
||||
|
||||
searchWidget->setText(text);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioDatabaseWidget::handleTrackSelected(boost::filesystem::path p)
|
||||
{
|
||||
_trackSelected.emit(p);
|
||||
}
|
||||
|
||||
void
|
||||
AudioDatabaseWidget::handleFilterUpdated(std::size_t idFilterUpdated)
|
||||
{
|
||||
// TODO disconnect from event!
|
||||
if (_refreshingFilters)
|
||||
return;
|
||||
|
||||
_refreshingFilters = true;
|
||||
|
||||
FilterWidget::Constraint currentConstraint;
|
||||
|
||||
currentConstraint.where.And( WhereClause( "track.artist_id = artist.id and track.release_id = release.id and track_genre.track_id = track.id and genre.id = track_genre.genre_id"));
|
||||
|
||||
for (std::size_t idFilter = 0; idFilter < _filters.size(); ++idFilter)
|
||||
{
|
||||
FilterWidget* filter = _filters.at(idFilter);
|
||||
|
||||
// Apply contraints created by previous filters
|
||||
if (idFilter > idFilterUpdated) {
|
||||
filter->refresh(currentConstraint);
|
||||
}
|
||||
|
||||
// Get constraints generated by this filter
|
||||
// (Note: adding accross successive calls)
|
||||
filter->getConstraint(currentConstraint);
|
||||
}
|
||||
|
||||
_refreshingFilters = false;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
AudioDatabaseWidget::selectNextTrack(void)
|
||||
{
|
||||
TrackWidget* trackWidget ( dynamic_cast<TrackWidget*>(_filters.back() ) );
|
||||
|
||||
trackWidget->selectNextTrack();
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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/>.
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_DB_WIDGET_HPP
|
||||
#define AUDIO_DB_WIDGET_HPP
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <Wt/WContainerWidget>
|
||||
#include <Wt/WSignal>
|
||||
|
||||
#include "common/SessionData.hpp"
|
||||
|
||||
#include "FilterWidget.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class AudioDatabaseWidget : public Wt::WContainerWidget
|
||||
{
|
||||
public:
|
||||
AudioDatabaseWidget( Database::Handler& db, Wt::WContainerWidget *parent = 0);
|
||||
|
||||
void search(const std::string& text);
|
||||
|
||||
void selectNextTrack(void); // Will later emit the next selected track
|
||||
|
||||
// Signals
|
||||
Wt::Signal< boost::filesystem::path >& trackSelected() { return _trackSelected; }
|
||||
|
||||
private:
|
||||
|
||||
Wt::Signal< boost::filesystem::path > _trackSelected;
|
||||
|
||||
void handleTrackSelected(boost::filesystem::path p);
|
||||
void handleFilterUpdated(std::size_t idFilter);
|
||||
|
||||
std::vector<FilterWidget*> _filters; // Free Search, Genre, Artist, Release, etc.
|
||||
|
||||
bool _refreshingFilters;
|
||||
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,10 +17,20 @@
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Wt/WBreak>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include <Wt/WVBoxLayout>
|
||||
#include <Wt/WHBoxLayout>
|
||||
#include <Wt/WGridLayout>
|
||||
#include <Wt/WComboBox>
|
||||
#include <Wt/WPushButton>
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
|
||||
#include "TableFilter.hpp"
|
||||
#include "KeywordSearchFilter.hpp"
|
||||
#include "TrackView.hpp"
|
||||
|
||||
#include "AudioWidget.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
@@ -28,33 +38,80 @@ namespace UserInterface {
|
||||
AudioWidget::AudioWidget(SessionData& sessionData, Wt::WContainerWidget* parent)
|
||||
: Wt::WContainerWidget(parent),
|
||||
_db(sessionData.getDatabaseHandler()),
|
||||
_audioDbWidget(nullptr),
|
||||
_mediaPlayer(nullptr),
|
||||
_imgResource(nullptr),
|
||||
_img(nullptr)
|
||||
_mediaPlayer(nullptr)
|
||||
{
|
||||
_audioDbWidget = new AudioDatabaseWidget(sessionData.getDatabaseHandler(), this);
|
||||
|
||||
_audioDbWidget->trackSelected().connect(this, &AudioWidget::playTrack);
|
||||
Wt::WGridLayout *mainLayout = new Wt::WGridLayout();
|
||||
this->setLayout(mainLayout);
|
||||
|
||||
// Filters
|
||||
Wt::WHBoxLayout *filterLayout = new Wt::WHBoxLayout();
|
||||
|
||||
{
|
||||
TableFilter *filter = new TableFilter(_db, "genre", "name");
|
||||
filterLayout->addWidget(filter);
|
||||
_filterChain.addFilter(filter);
|
||||
}
|
||||
{
|
||||
TableFilter *filter = new TableFilter(_db, "artist", "name");
|
||||
filterLayout->addWidget(filter);
|
||||
_filterChain.addFilter(filter);
|
||||
}
|
||||
{
|
||||
TableFilter *filter = new TableFilter(_db, "release", "name");
|
||||
filterLayout->addWidget(filter);
|
||||
_filterChain.addFilter(filter);
|
||||
}
|
||||
|
||||
mainLayout->addLayout(filterLayout, 0, 0);
|
||||
// ENDOF(Filters)
|
||||
|
||||
TrackView* trackView = new TrackView(_db);
|
||||
mainLayout->addWidget( trackView, 1, 0);
|
||||
_filterChain.addFilter(trackView);
|
||||
|
||||
// TODO Playlist here
|
||||
{
|
||||
Wt::WVBoxLayout* playlist = new Wt::WVBoxLayout();
|
||||
|
||||
Wt::WHBoxLayout* playlistControls = new Wt::WHBoxLayout();
|
||||
|
||||
Wt::WComboBox *cb = new Wt::WComboBox();
|
||||
cb->addItem("metal");
|
||||
cb->addItem("rock");
|
||||
cb->addItem("top50");
|
||||
cb->setWidth(100);
|
||||
|
||||
playlistControls->addWidget(cb, 1);
|
||||
playlistControls->addWidget(new Wt::WPushButton("Rename"));
|
||||
playlistControls->addWidget(new Wt::WPushButton("+"));
|
||||
playlistControls->addWidget(new Wt::WPushButton("-"));
|
||||
|
||||
playlist->addLayout(playlistControls);
|
||||
|
||||
playlist->addWidget( new TableFilter(_db, "release", "name"), 1);
|
||||
|
||||
mainLayout->addLayout(playlist, 0, 1, 2, 1);
|
||||
}
|
||||
|
||||
_mediaPlayer = new AudioMediaPlayerWidget();
|
||||
mainLayout->addWidget(_mediaPlayer, 2, 0, 1, 2);
|
||||
|
||||
mainLayout->setColumnStretch(0, 1);
|
||||
mainLayout->setRowStretch(1, 1);
|
||||
mainLayout->setRowResizable(0, true, Wt::WLength(200, Wt::WLength::Pixel));
|
||||
mainLayout->setColumnResizable(0, true);
|
||||
|
||||
_mediaPlayer = new AudioMediaPlayerWidget(this);
|
||||
_mediaPlayer->playbackEnded().connect(this, &AudioWidget::handleTrackEnded);
|
||||
this->addWidget(new Wt::WBreak());
|
||||
|
||||
// Image
|
||||
_imgResource = new Wt::WMemoryResource(this);
|
||||
_imgLink.setResource( _imgResource);
|
||||
_img = new Wt::WImage(_imgLink, this);
|
||||
|
||||
trackView->trackSelected().connect(boost::bind(&AudioWidget::playTrack, this, _1));
|
||||
}
|
||||
|
||||
void
|
||||
AudioWidget::search(const std::string& searchText)
|
||||
{
|
||||
_audioDbWidget->search(searchText);
|
||||
_filterChain.searchKeyword(searchText);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioWidget::playTrack(boost::filesystem::path p)
|
||||
{
|
||||
@@ -71,7 +128,7 @@ AudioWidget::playTrack(boost::filesystem::path p)
|
||||
bitrate = user->getAudioBitrate();
|
||||
else
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_ERROR) << "Can't play video: user does not exists!";
|
||||
LMS_LOG(MOD_UI, SEV_ERROR) << "Can't play: user does not exists!";
|
||||
return; // TODO logout?
|
||||
}
|
||||
}
|
||||
@@ -83,27 +140,6 @@ AudioWidget::playTrack(boost::filesystem::path p)
|
||||
parameters.setBitrate(Transcode::Stream::Audio, bitrate);
|
||||
|
||||
_mediaPlayer->load( parameters );
|
||||
|
||||
// Refresh cover
|
||||
{
|
||||
std::vector<CoverArt::CoverArt> covers = inputFile.getCovers();
|
||||
|
||||
if (!covers.empty())
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "Cover found!";
|
||||
if (!covers.front().scale(256)) // TODO
|
||||
LMS_LOG(MOD_UI, SEV_ERROR) << "Cannot resize!";
|
||||
|
||||
//_imgResource->setMimeType(covers.front().getMimeType());
|
||||
_imgResource->setData(covers.front().getData());
|
||||
}
|
||||
else {
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "No cover found!";
|
||||
_imgResource->setData( std::vector<unsigned char>());
|
||||
}
|
||||
|
||||
_imgResource->setChanged();
|
||||
}
|
||||
}
|
||||
catch( std::exception &e)
|
||||
{
|
||||
@@ -111,11 +147,10 @@ AudioWidget::playTrack(boost::filesystem::path p)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
AudioWidget::handleTrackEnded(void)
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "Track playback ended!";
|
||||
_audioDbWidget->selectNextTrack();
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
@@ -22,13 +22,13 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <Wt/WLink>
|
||||
#include <Wt/WImage>
|
||||
#include <Wt/WContainerWidget>
|
||||
#include <Wt/WMemoryResource>
|
||||
|
||||
#include "audio/AudioMediaPlayerWidget.hpp"
|
||||
#include "audio/AudioDatabaseWidget.hpp"
|
||||
#include "common/SessionData.hpp"
|
||||
|
||||
#include "AudioMediaPlayerWidget.hpp"
|
||||
|
||||
#include "FilterChain.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
@@ -49,16 +49,9 @@ class AudioWidget : public Wt::WContainerWidget
|
||||
|
||||
Database::Handler& _db;
|
||||
|
||||
AudioDatabaseWidget* _audioDbWidget;
|
||||
|
||||
AudioMediaPlayerWidget* _mediaPlayer;
|
||||
|
||||
// Image
|
||||
Wt::WMemoryResource *_imgResource;
|
||||
Wt::WLink _imgLink;
|
||||
Wt::WImage *_img;
|
||||
|
||||
|
||||
FilterChain _filterChain;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -17,35 +17,30 @@
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FILTER_WIDGET_HPP
|
||||
#define FILTER_WIDGET_HPP
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <string>
|
||||
#include <list>
|
||||
#ifndef FILTER_HPP
|
||||
#define FILTER_HPP
|
||||
|
||||
#include <Wt/WSignal>
|
||||
#include <Wt/WContainerWidget>
|
||||
|
||||
#include "database/SqlQuery.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class FilterWidget : public Wt::WContainerWidget {
|
||||
|
||||
class Filter
|
||||
{
|
||||
public:
|
||||
|
||||
struct Constraint {
|
||||
WhereClause where; // WHERE SQL clause
|
||||
};
|
||||
|
||||
FilterWidget(Wt::WContainerWidget* parent = 0) : Wt::WContainerWidget(parent) {}
|
||||
virtual ~FilterWidget() {}
|
||||
Filter() {}
|
||||
virtual ~Filter() {}
|
||||
|
||||
// Refresh filter Widget using constraints created by parent filters
|
||||
// Refresh filter using constraints created by parent filters
|
||||
virtual void refresh(const Constraint& constraint) = 0;
|
||||
|
||||
// Update constraints for child filters
|
||||
// Update constraints for next filters
|
||||
virtual void getConstraint(Constraint& constraint) = 0;
|
||||
|
||||
// Emitted when a constraint has changed
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 "FilterChain.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
|
||||
FilterChain::FilterChain()
|
||||
: _refreshingFilters(false)
|
||||
{
|
||||
addFilter(&_keywordSearchFilter);
|
||||
}
|
||||
|
||||
void
|
||||
FilterChain::addFilter(Filter* filter)
|
||||
{
|
||||
filter->update().connect(boost::bind(&FilterChain::updateFilters, this, _filters.size()));
|
||||
_filters.push_back(filter);
|
||||
}
|
||||
|
||||
void
|
||||
FilterChain::searchKeyword(const std::string& text)
|
||||
{
|
||||
_keywordSearchFilter.setText(text);
|
||||
}
|
||||
|
||||
void
|
||||
FilterChain::updateFilters(std::size_t startIdx)
|
||||
{
|
||||
// Prevent loops
|
||||
if (_refreshingFilters)
|
||||
return;
|
||||
|
||||
_refreshingFilters = true;
|
||||
|
||||
Filter::Constraint currentConstraint;
|
||||
|
||||
// TODO: replace with inner joins?
|
||||
currentConstraint.where.And( WhereClause( "track.artist_id = artist.id and track.release_id = release.id and track_genre.track_id = track.id and genre.id = track_genre.genre_id"));
|
||||
|
||||
for (std::size_t idFilter = 0; idFilter < _filters.size(); ++idFilter)
|
||||
{
|
||||
Filter* filter = _filters.at(idFilter);
|
||||
|
||||
// Apply contraints created by previous filters
|
||||
if (idFilter > startIdx) {
|
||||
filter->refresh(currentConstraint);
|
||||
}
|
||||
|
||||
// Get constraints generated by this filter
|
||||
// (Note: adding accross successive calls)
|
||||
filter->getConstraint(currentConstraint);
|
||||
}
|
||||
|
||||
_refreshingFilters = false;
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef FILTER_CHAIN_HPP
|
||||
#define FILTER_CHAIN_HPP
|
||||
|
||||
#include <Wt/WSignal>
|
||||
|
||||
#include "Filter.hpp"
|
||||
#include "KeywordSearchFilter.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
// FilterChain
|
||||
class FilterChain
|
||||
{
|
||||
public:
|
||||
|
||||
FilterChain();
|
||||
|
||||
void addFilter(Filter* filter);
|
||||
|
||||
// First filter is a keywork search
|
||||
void searchKeyword(const std::string& text);
|
||||
|
||||
// Update filters from filter @ startIdx
|
||||
void updateFilters(std::size_t startIdx);
|
||||
|
||||
private:
|
||||
|
||||
KeywordSearchFilter _keywordSearchFilter;
|
||||
|
||||
// No ownership
|
||||
std::vector<Filter*> _filters;
|
||||
|
||||
bool _refreshingFilters;
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
@@ -17,19 +17,16 @@
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Wt/WLabel>
|
||||
|
||||
#include "SearchFilterWidget.hpp"
|
||||
#include "KeywordSearchFilter.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
SearchFilterWidget::SearchFilterWidget(Wt::WContainerWidget* parent)
|
||||
: FilterWidget( parent )
|
||||
KeywordSearchFilter::KeywordSearchFilter()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
SearchFilterWidget::setText(const std::string& text)
|
||||
KeywordSearchFilter::setText(const std::string& text)
|
||||
{
|
||||
_lastEmittedText = text;
|
||||
emitUpdate();
|
||||
@@ -37,7 +34,7 @@ SearchFilterWidget::setText(const std::string& text)
|
||||
|
||||
// Get constraints created by this filter
|
||||
void
|
||||
SearchFilterWidget::getConstraint(Constraint& constraint)
|
||||
KeywordSearchFilter::getConstraint(Constraint& constraint)
|
||||
{
|
||||
// No active search means no constaint!
|
||||
if (!_lastEmittedText.empty()) {
|
||||
@@ -17,18 +17,19 @@
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SEARCH_FILTER_WIDGET_PP
|
||||
#define SEARCH_FILTER_WIDGET_PP
|
||||
#ifndef KEYWORD_SEARCH_FILTER_HPP
|
||||
#define KEYWORD_SEARCH_FILTER_HPP
|
||||
|
||||
#include <Wt/WLineEdit>
|
||||
#include <string>
|
||||
|
||||
#include "FilterWidget.hpp"
|
||||
#include "Filter.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class SearchFilterWidget : public FilterWidget {
|
||||
class KeywordSearchFilter : public Filter
|
||||
{
|
||||
public:
|
||||
SearchFilterWidget(Wt::WContainerWidget* parent = 0);
|
||||
KeywordSearchFilter();
|
||||
|
||||
void setText(const std::string& text);
|
||||
|
||||
@@ -21,36 +21,49 @@
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
|
||||
#include "TableFilterWidget.hpp"
|
||||
#include "TableFilter.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
TableFilterWidget::TableFilterWidget(Database::Handler& db, std::string table, std::string field, Wt::WContainerWidget* parent)
|
||||
: FilterWidget( parent ),
|
||||
TableFilter::TableFilter(Database::Handler& db, std::string table, std::string field, Wt::WContainerWidget* parent)
|
||||
: Wt::WTableView( parent ),
|
||||
Filter(),
|
||||
_db(db),
|
||||
_table(table),
|
||||
_field(field),
|
||||
_tableView(nullptr)
|
||||
_field(field)
|
||||
{
|
||||
_queryModel.setQuery( _db.getSession().query< ResultType >("select " + _table + "." + _field + ",count(DISTINCT track.id),0 as ORDERBY from track,artist,release,genre,track_genre WHERE track.artist_id = artist.id and track.release_id = release.id and track_genre.track_id = track.id and genre.id = track_genre.genre_id GROUP BY " + _table + "." + _field + " UNION select '<All>',0,1 AS ORDERBY").orderBy("ORDERBY DESC," + _table + "." + _field));
|
||||
_queryModel.addColumn( _table + "." + _field, table);
|
||||
_queryModel.addColumn( "count(DISTINCT track.id)", "Tracks");
|
||||
|
||||
_tableView = new Wt::WTableView( this );
|
||||
_tableView->resize(250, 200); // TODO
|
||||
_tableView->setSelectionMode(Wt::ExtendedSelection);
|
||||
_tableView->setSortingEnabled(false);
|
||||
_tableView->setAlternatingRowColors(true);
|
||||
_tableView->setModel(&_queryModel);
|
||||
this->setSelectionMode(Wt::ExtendedSelection);
|
||||
this->setSortingEnabled(false);
|
||||
this->setAlternatingRowColors(true);
|
||||
this->setModel(&_queryModel);
|
||||
|
||||
_tableView->selectionChanged().connect(this, &TableFilterWidget::emitUpdate);
|
||||
this->selectionChanged().connect(this, &TableFilter::emitUpdate);
|
||||
|
||||
setLayoutSizeAware(true);
|
||||
|
||||
_queryModel.setBatchSize(100);
|
||||
}
|
||||
|
||||
void
|
||||
TableFilter::layoutSizeChanged (int width, int height)
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "LAYOUT CHANGED!";
|
||||
|
||||
/* TODO
|
||||
std::size_t trackColumnSize = this->columnWidth(1).toPixels() + 30 ;
|
||||
// Set the remaining size for the name column
|
||||
this->setColumnWidth(0, width - 7 - trackColumnSize);
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
// Set constraints on this filter
|
||||
void
|
||||
TableFilterWidget::refresh(const Constraint& constraint)
|
||||
TableFilter::refresh(const Constraint& constraint)
|
||||
{
|
||||
SqlQuery sqlQuery;
|
||||
|
||||
@@ -73,16 +86,16 @@ TableFilterWidget::refresh(const Constraint& constraint)
|
||||
query.bind(bindArg);
|
||||
}
|
||||
|
||||
_queryModel.setQuery( query, true /* Keep columns */);
|
||||
_queryModel.setQuery( query, true );
|
||||
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "Finish !";
|
||||
}
|
||||
|
||||
// Get constraint created by this filter
|
||||
void
|
||||
TableFilterWidget::getConstraint(Constraint& constraint)
|
||||
TableFilter::getConstraint(Constraint& constraint)
|
||||
{
|
||||
Wt::WModelIndexSet indexSet = _tableView->selectedIndexes();
|
||||
Wt::WModelIndexSet indexSet = this->selectedIndexes();
|
||||
|
||||
// WHERE statement
|
||||
WhereClause clause;
|
||||
@@ -17,29 +17,31 @@
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TABLE_VIEW_FILTER_WIDGET_HPP
|
||||
#define TABLE_VIEW_FILTER_WIDGET_HPP
|
||||
#ifndef TABLE_FILTER_HPP
|
||||
#define TABLE_FILTER_HPP
|
||||
|
||||
|
||||
#include <Wt/Dbo/QueryModel>
|
||||
#include <Wt/WTableView>
|
||||
|
||||
#include "FilterWidget.hpp"
|
||||
#include "Filter.hpp"
|
||||
#include "database/DatabaseHandler.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class TableFilterWidget : public FilterWidget
|
||||
class TableFilter : public Wt::WTableView, public Filter
|
||||
{
|
||||
|
||||
public:
|
||||
TableFilterWidget(Database::Handler& db, std::string table, std::string field, Wt::WContainerWidget* parent = 0);
|
||||
TableFilter(Database::Handler& db, std::string table, std::string field, Wt::WContainerWidget* parent = 0);
|
||||
|
||||
// Set constraints on this filter
|
||||
virtual void refresh(const Constraint& constraint);
|
||||
void refresh(const Constraint& constraint);
|
||||
|
||||
// Get constraints created by this filter
|
||||
virtual void getConstraint(Constraint& constraint);
|
||||
void getConstraint(Constraint& constraint);
|
||||
|
||||
void layoutSizeChanged (int width, int height);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -51,7 +53,6 @@ class TableFilterWidget : public FilterWidget
|
||||
typedef boost::tuple<std::string, int, int> ResultType;
|
||||
Wt::Dbo::QueryModel< ResultType > _queryModel;
|
||||
|
||||
Wt::WTableView* _tableView;
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
@@ -25,18 +25,14 @@
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
|
||||
#include "TrackWidget.hpp"
|
||||
#include "TrackView.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
TrackWidget::TrackWidget( Database::Handler& db, Wt::WContainerWidget* parent)
|
||||
: FilterWidget( parent ),
|
||||
_db(db),
|
||||
_tableView(nullptr),
|
||||
_trackStats(nullptr)
|
||||
TrackView::TrackView( Database::Handler& db, Wt::WContainerWidget* parent)
|
||||
: Wt::WTableView( parent ),
|
||||
_db(db)
|
||||
{
|
||||
|
||||
// ----- TRACK -----
|
||||
_queryModel.setQuery(_db.getSession().query<ResultType>("select track,release,artist from track,release,artist where track.release_id = release.id and track.artist_id = artist.id" ).orderBy("artist.name,release.name,track.disc_number,track.track_number"));
|
||||
_queryModel.addColumn( "artist.name", "Artist" );
|
||||
_queryModel.addColumn( "release.name", "Album" );
|
||||
@@ -48,56 +44,52 @@ _trackStats(nullptr)
|
||||
_queryModel.addColumn( "track.original_date", "Original Date" );
|
||||
_queryModel.addColumn( "track.genre_list", "Genres" );
|
||||
|
||||
_queryModel.setBatchSize(1000);
|
||||
_queryModel.setBatchSize(250);
|
||||
|
||||
_tableView = new Wt::WTableView( this );
|
||||
_tableView->resize(Wt::WLength::Auto, 400);
|
||||
this->setSortingEnabled(true);
|
||||
this->setSelectionMode(Wt::SingleSelection);
|
||||
this->setAlternatingRowColors(true);
|
||||
this->setModel(&_queryModel);
|
||||
|
||||
_tableView->setSortingEnabled(true);
|
||||
_tableView->setSelectionMode(Wt::SingleSelection);
|
||||
_tableView->setAlternatingRowColors(true);
|
||||
_tableView->setModel(&_queryModel);
|
||||
this->setColumnWidth(0, 180); // Artist
|
||||
this->setColumnWidth(1, 180); // Album
|
||||
this->setColumnWidth(2, 70); // Disc Number
|
||||
this->setColumnWidth(3, 70); // Track Number
|
||||
this->setColumnWidth(4, 180); // Track
|
||||
this->setColumnWidth(5, 70); // Duration
|
||||
this->setColumnWidth(6, 70); // Date
|
||||
this->setColumnWidth(7, 70); // Original Date
|
||||
this->setColumnWidth(8, 180); // Genres
|
||||
|
||||
// this->setOverflow(Wt::WContainerWidget::OverflowScroll, Wt::Vertical);
|
||||
|
||||
// Duration display
|
||||
{
|
||||
// TODO better handle 1 hour+ files!
|
||||
Wt::WItemDelegate *delegate = new Wt::WItemDelegate(this);
|
||||
delegate->setTextFormat("mm:ss");
|
||||
_tableView->setItemDelegateForColumn(5, delegate);
|
||||
this->setItemDelegateForColumn(5, delegate);
|
||||
}
|
||||
|
||||
// Date display, just the year
|
||||
{
|
||||
Wt::WItemDelegate *delegate = new Wt::WItemDelegate(this);
|
||||
delegate->setTextFormat("yyyy");
|
||||
_tableView->setItemDelegateForColumn(6, delegate);
|
||||
this->setItemDelegateForColumn(6, delegate);
|
||||
}
|
||||
{
|
||||
Wt::WItemDelegate *delegate = new Wt::WItemDelegate(this);
|
||||
delegate->setTextFormat("yyyy");
|
||||
_tableView->setItemDelegateForColumn(7, delegate);
|
||||
this->setItemDelegateForColumn(7, delegate);
|
||||
}
|
||||
|
||||
// TODO other event!
|
||||
_tableView->selectionChanged().connect(this, &TrackWidget::handleTrackSelected);
|
||||
|
||||
new Wt::WBreak(this);
|
||||
_trackStats = new Wt::WText(this);
|
||||
|
||||
updateStats();
|
||||
}
|
||||
|
||||
void
|
||||
TrackWidget::updateStats(void)
|
||||
{
|
||||
std::ostringstream oss; oss << "Files :" << _tableView->model()->rowCount();
|
||||
// TODO from UTF-8 ?
|
||||
_trackStats->setText(oss.str());
|
||||
this->selectionChanged().connect(this, &TrackView::handleTrackSelected);
|
||||
}
|
||||
|
||||
// Set constraints created by parent filters
|
||||
void
|
||||
TrackWidget::refresh(const Constraint& constraint)
|
||||
TrackView::refresh(const Constraint& constraint)
|
||||
{
|
||||
|
||||
SqlQuery sqlQuery;
|
||||
@@ -119,13 +111,12 @@ TrackWidget::refresh(const Constraint& constraint)
|
||||
|
||||
_queryModel.setQuery( query, true );
|
||||
|
||||
updateStats();
|
||||
}
|
||||
|
||||
void
|
||||
TrackWidget::handleTrackSelected(void)
|
||||
TrackView::handleTrackSelected(void)
|
||||
{
|
||||
Wt::WModelIndexSet indexSet = _tableView->selectedIndexes();
|
||||
Wt::WModelIndexSet indexSet = this->selectedIndexes();
|
||||
if (!indexSet.empty()) {
|
||||
Wt::WModelIndex currentIndex( *indexSet.begin() );
|
||||
|
||||
@@ -144,15 +135,15 @@ TrackWidget::handleTrackSelected(void)
|
||||
}
|
||||
|
||||
void
|
||||
TrackWidget::selectNextTrack(void)
|
||||
TrackView::selectNextTrack(void)
|
||||
{
|
||||
Wt::WModelIndexSet indexSet = _tableView->selectedIndexes();
|
||||
Wt::WModelIndexSet indexSet = this->selectedIndexes();
|
||||
if (!indexSet.empty()) {
|
||||
Wt::WModelIndex currentIndex( *indexSet.begin() );
|
||||
// Check there are remainin tracks!
|
||||
if (currentIndex.isValid() && _tableView->model()->rowCount() > currentIndex.row() + 1)
|
||||
if (currentIndex.isValid() && this->model()->rowCount() > currentIndex.row() + 1)
|
||||
{
|
||||
_tableView->select( _tableView->model()->index( currentIndex.row() + 1, currentIndex.column()));
|
||||
this->select( this->model()->index( currentIndex.row() + 1, currentIndex.column()));
|
||||
|
||||
ResultType result = _queryModel.resultRow( currentIndex.row() + 1 );
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TRACK_WIDGET_HPP
|
||||
#define TRACK_WIDGET_HPP
|
||||
#ifndef TRACK_HPP
|
||||
#define TRACK_HPP
|
||||
|
||||
#include <Wt/WTableView>
|
||||
#include <Wt/Dbo/QueryModel>
|
||||
@@ -26,15 +26,15 @@
|
||||
#include "database/DatabaseHandler.hpp"
|
||||
#include "database/AudioTypes.hpp"
|
||||
|
||||
#include "FilterWidget.hpp"
|
||||
#include "Filter.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class TrackWidget : public FilterWidget
|
||||
class TrackView : public Wt::WTableView, public Filter
|
||||
{
|
||||
public:
|
||||
|
||||
TrackWidget( Database::Handler& db, Wt::WContainerWidget* parent = 0);
|
||||
TrackView( Database::Handler& db, Wt::WContainerWidget* parent = 0);
|
||||
|
||||
// Set constraints created by parent filters
|
||||
void refresh(const Constraint& constraint);
|
||||
@@ -59,10 +59,6 @@ class TrackWidget : public FilterWidget
|
||||
Wt::Dbo::QueryModel< ResultType > _queryModel;
|
||||
Wt::WTableView* _tableView;
|
||||
|
||||
void updateStats(void);
|
||||
|
||||
Wt::WText* _trackStats;
|
||||
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
@@ -750,7 +750,7 @@ std::set<Test> tests = {
|
||||
Test::ReleaseFilterGenre,
|
||||
// Test::TrackFilters,
|
||||
// Test::Cover,
|
||||
// Test::Transcode,
|
||||
Test::Transcode,
|
||||
};
|
||||
|
||||
bool test(Test t)
|
||||
|
||||
Reference in New Issue
Block a user