WIP. new layout on the audio view
This commit is contained in:
+47
-47
@@ -1,64 +1,64 @@
|
|||||||
bin_PROGRAMS = lms
|
bin_PROGRAMS = lms
|
||||||
|
|
||||||
lms_SOURCES = \
|
lms_SOURCES = \
|
||||||
$(srcdir)/main/main.cpp \
|
$(srcdir)/main/main.cpp \
|
||||||
$(srcdir)/av/CodecContext.cpp \
|
$(srcdir)/av/CodecContext.cpp \
|
||||||
$(srcdir)/av/Common.cpp \
|
$(srcdir)/av/Common.cpp \
|
||||||
$(srcdir)/av/Dictionary.cpp \
|
$(srcdir)/av/Dictionary.cpp \
|
||||||
$(srcdir)/av/FormatContext.cpp \
|
$(srcdir)/av/FormatContext.cpp \
|
||||||
$(srcdir)/av/InputFormatContext.cpp \
|
$(srcdir)/av/InputFormatContext.cpp \
|
||||||
$(srcdir)/av/Stream.cpp \
|
$(srcdir)/av/Stream.cpp \
|
||||||
$(srcdir)/config/ConfigReader.cpp \
|
$(srcdir)/config/ConfigReader.cpp \
|
||||||
$(srcdir)/cover/CoverArt.cpp \
|
$(srcdir)/cover/CoverArt.cpp \
|
||||||
$(srcdir)/cover/CoverArtGrabber.cpp \
|
$(srcdir)/cover/CoverArtGrabber.cpp \
|
||||||
$(srcdir)/database/Artist.cpp \
|
$(srcdir)/database/Artist.cpp \
|
||||||
$(srcdir)/database/Genre.cpp \
|
$(srcdir)/database/Genre.cpp \
|
||||||
$(srcdir)/database/Release.cpp \
|
$(srcdir)/database/Release.cpp \
|
||||||
$(srcdir)/database/MediaDirectory.cpp \
|
$(srcdir)/database/MediaDirectory.cpp \
|
||||||
$(srcdir)/database/Track.cpp \
|
$(srcdir)/database/Track.cpp \
|
||||||
$(srcdir)/database/DatabaseHandler.cpp \
|
$(srcdir)/database/DatabaseHandler.cpp \
|
||||||
$(srcdir)/database/SqlQuery.cpp \
|
$(srcdir)/database/SqlQuery.cpp \
|
||||||
$(srcdir)/database/Video.cpp \
|
$(srcdir)/database/Video.cpp \
|
||||||
$(srcdir)/database/User.cpp \
|
$(srcdir)/database/User.cpp \
|
||||||
$(srcdir)/database-updater/DatabaseUpdater.cpp \
|
$(srcdir)/database-updater/DatabaseUpdater.cpp \
|
||||||
$(srcdir)/database-updater/Checksum.cpp \
|
$(srcdir)/database-updater/Checksum.cpp \
|
||||||
$(srcdir)/logger/Logger.cpp \
|
$(srcdir)/logger/Logger.cpp \
|
||||||
$(srcdir)/metadata/AvFormat.cpp \
|
$(srcdir)/metadata/AvFormat.cpp \
|
||||||
$(srcdir)/metadata/Utils.cpp \
|
$(srcdir)/metadata/Utils.cpp \
|
||||||
$(srcdir)/remote/server/Connection.cpp \
|
$(srcdir)/remote/server/Connection.cpp \
|
||||||
$(srcdir)/remote/server/ConnectionManager.cpp \
|
$(srcdir)/remote/server/ConnectionManager.cpp \
|
||||||
$(srcdir)/remote/server/AudioCollectionRequestHandler.cpp \
|
$(srcdir)/remote/server/AudioCollectionRequestHandler.cpp \
|
||||||
$(srcdir)/remote/server/AuthRequestHandler.cpp \
|
$(srcdir)/remote/server/AuthRequestHandler.cpp \
|
||||||
$(srcdir)/remote/server/MediaRequestHandler.cpp \
|
$(srcdir)/remote/server/MediaRequestHandler.cpp \
|
||||||
$(srcdir)/remote/server/RequestHandler.cpp \
|
$(srcdir)/remote/server/RequestHandler.cpp \
|
||||||
$(srcdir)/remote/server/Server.cpp \
|
$(srcdir)/remote/server/Server.cpp \
|
||||||
$(srcdir)/service/ServiceManager.cpp \
|
$(srcdir)/service/ServiceManager.cpp \
|
||||||
$(srcdir)/service/DatabaseUpdateService.cpp \
|
$(srcdir)/service/DatabaseUpdateService.cpp \
|
||||||
$(srcdir)/service/UserInterfaceService.cpp \
|
$(srcdir)/service/UserInterfaceService.cpp \
|
||||||
$(srcdir)/service/RemoteServerService.cpp \
|
$(srcdir)/service/RemoteServerService.cpp \
|
||||||
$(srcdir)/transcode/AvConvTranscoder.cpp \
|
$(srcdir)/transcode/AvConvTranscoder.cpp \
|
||||||
$(srcdir)/transcode/Format.cpp \
|
$(srcdir)/transcode/Format.cpp \
|
||||||
$(srcdir)/transcode/Parameters.cpp \
|
$(srcdir)/transcode/Parameters.cpp \
|
||||||
$(srcdir)/transcode/InputMediaFile.cpp \
|
$(srcdir)/transcode/InputMediaFile.cpp \
|
||||||
$(srcdir)/ui/LmsApplication.cpp \
|
$(srcdir)/ui/LmsApplication.cpp \
|
||||||
$(srcdir)/ui/LmsHome.cpp \
|
$(srcdir)/ui/LmsHome.cpp \
|
||||||
$(srcdir)/ui/auth/LmsAuth.cpp \
|
$(srcdir)/ui/auth/LmsAuth.cpp \
|
||||||
$(srcdir)/ui/audio/AudioWidget.cpp \
|
$(srcdir)/ui/audio/AudioWidget.cpp \
|
||||||
$(srcdir)/ui/audio/AudioDatabaseWidget.cpp \
|
$(srcdir)/ui/audio/AudioMediaPlayerWidget.cpp \
|
||||||
$(srcdir)/ui/audio/AudioMediaPlayerWidget.cpp \
|
$(srcdir)/ui/audio/FilterChain.cpp \
|
||||||
$(srcdir)/ui/audio/SearchFilterWidget.cpp \
|
$(srcdir)/ui/audio/KeywordSearchFilter.cpp \
|
||||||
$(srcdir)/ui/audio/TableFilterWidget.cpp \
|
$(srcdir)/ui/audio/TableFilter.cpp \
|
||||||
$(srcdir)/ui/audio/TrackWidget.cpp \
|
$(srcdir)/ui/audio/TrackView.cpp \
|
||||||
$(srcdir)/ui/common/DirectoryValidator.cpp \
|
$(srcdir)/ui/common/DirectoryValidator.cpp \
|
||||||
$(srcdir)/ui/common/SessionData.cpp \
|
$(srcdir)/ui/common/SessionData.cpp \
|
||||||
$(srcdir)/ui/resource/AvConvTranscodeStreamResource.cpp \
|
$(srcdir)/ui/resource/AvConvTranscodeStreamResource.cpp \
|
||||||
$(srcdir)/ui/video/VideoWidget.cpp \
|
$(srcdir)/ui/video/VideoWidget.cpp \
|
||||||
$(srcdir)/ui/video/VideoDatabaseWidget.cpp \
|
$(srcdir)/ui/video/VideoDatabaseWidget.cpp \
|
||||||
$(srcdir)/ui/video/VideoMediaPlayerWidget.cpp \
|
$(srcdir)/ui/video/VideoMediaPlayerWidget.cpp \
|
||||||
$(srcdir)/ui/video/VideoParametersDialog.cpp \
|
$(srcdir)/ui/video/VideoParametersDialog.cpp \
|
||||||
$(srcdir)/ui/settings/Settings.cpp \
|
$(srcdir)/ui/settings/Settings.cpp \
|
||||||
$(srcdir)/ui/settings/SettingsAccountFormView.cpp \
|
$(srcdir)/ui/settings/SettingsAccountFormView.cpp \
|
||||||
$(srcdir)/ui/settings/SettingsAudioFormView.cpp \
|
$(srcdir)/ui/settings/SettingsAudioFormView.cpp \
|
||||||
$(srcdir)/ui/settings/SettingsDatabaseFormView.cpp \
|
$(srcdir)/ui/settings/SettingsDatabaseFormView.cpp \
|
||||||
$(srcdir)/ui/settings/SettingsFirstConnectionFormView.cpp \
|
$(srcdir)/ui/settings/SettingsFirstConnectionFormView.cpp \
|
||||||
$(srcdir)/ui/settings/SettingsMediaDirectories.cpp \
|
$(srcdir)/ui/settings/SettingsMediaDirectories.cpp \
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ AuthRequestHandler::processPassword(const AuthRequest::Password& request, AuthRe
|
|||||||
{
|
{
|
||||||
case Wt::Auth::PasswordInvalid:
|
case Wt::Auth::PasswordInvalid:
|
||||||
response.set_type(AuthResponse::PasswordResult::TypePasswordInvalid);
|
response.set_type(AuthResponse::PasswordResult::TypePasswordInvalid);
|
||||||
|
LMS_LOG(MOD_REMOTE, SEV_NOTICE) << "User '" << request.user_login() << "': invalid password";
|
||||||
res = true;
|
res = true;
|
||||||
break;
|
break;
|
||||||
case Wt::Auth::LoginThrottling:
|
case Wt::Auth::LoginThrottling:
|
||||||
@@ -81,9 +82,11 @@ AuthRequestHandler::processPassword(const AuthRequest::Password& request, AuthRe
|
|||||||
response.set_type(AuthResponse::PasswordResult::TypePasswordValid);
|
response.set_type(AuthResponse::PasswordResult::TypePasswordValid);
|
||||||
// Log the user in
|
// Log the user in
|
||||||
_db.getLogin().login( user );
|
_db.getLogin().login( user );
|
||||||
|
LMS_LOG(MOD_REMOTE, SEV_NOTICE) << "User '" << request.user_login() << "' successfully logged in";
|
||||||
res = true;
|
res = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
LMS_LOG(MOD_REMOTE, SEV_ERROR) << "Cannot handle password result!";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+50
-47
@@ -18,8 +18,24 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Wt/WBootstrapTheme>
|
#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 "auth/LmsAuth.hpp"
|
||||||
|
|
||||||
|
#include "logger/Logger.hpp"
|
||||||
|
|
||||||
#include "LmsHome.hpp"
|
#include "LmsHome.hpp"
|
||||||
#include "settings/SettingsFirstConnectionFormView.hpp"
|
#include "settings/SettingsFirstConnectionFormView.hpp"
|
||||||
|
|
||||||
@@ -64,65 +80,52 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env, boost::filesystem::p
|
|||||||
|
|
||||||
setTitle("LMS"); // application title
|
setTitle("LMS"); // application title
|
||||||
|
|
||||||
bool firstConnection;
|
// Create a Vertical layout: top is the nav bar, bottom is the contents
|
||||||
{
|
Wt::WVBoxLayout *layout = new Wt::WVBoxLayout(this->root());
|
||||||
Wt::Dbo::Transaction transaction(_sessionData.getDatabaseHandler().getSession());
|
// 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
|
// Setup a Left-aligned menu.
|
||||||
if (firstConnection)
|
Wt::WMenu *leftMenu = new Wt::WMenu(contentsStack);
|
||||||
{
|
navigation->addMenu(leftMenu);
|
||||||
// Hack, use the auth widget builtin strings
|
|
||||||
builtinLocalizedStrings().useBuiltin(skeletons::AuthStrings_xml1);
|
|
||||||
|
|
||||||
root()->addWidget( new Settings::FirstConnectionFormView(_sessionData));
|
_audioWidget = new AudioWidget(_sessionData);
|
||||||
}
|
_videoWidget = new VideoWidget(_sessionData);
|
||||||
else
|
|
||||||
{
|
|
||||||
_sessionData.getDatabaseHandler().getLogin().changed().connect(this, &LmsApplication::handleAuthEvent);
|
|
||||||
|
|
||||||
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());
|
// Setup a Right-aligned menu.
|
||||||
authWidget->setRegistrationEnabled(false);
|
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
|
// Add a Search control.
|
||||||
LmsApplication::handleAuthEvent(void)
|
/* _searchEdit = new Wt::WLineEdit();
|
||||||
{
|
_searchEdit->setEmptyText("Search...");
|
||||||
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;
|
|
||||||
|
|
||||||
// Hack: quit/redirect in order to avoid 'signal not exposed' problems
|
_searchEdit->enterPressed().connect(this, &LmsHome::handleSearch);
|
||||||
// TODO, investigate/remove?
|
*/
|
||||||
quit();
|
// navigation->addSearch(_searchEdit, Wt::AlignLeft);
|
||||||
redirect("/");
|
|
||||||
|
|
||||||
}
|
layout->addWidget(navigation);
|
||||||
else
|
layout->addWidget(contentsStack, 1);
|
||||||
std::cerr << "Already logged out??" << std::endl;
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ class LmsApplication : public Wt::WApplication
|
|||||||
SessionData _sessionData;
|
SessionData _sessionData;
|
||||||
|
|
||||||
LmsHome* _home;
|
LmsHome* _home;
|
||||||
|
AudioWidget* _audioWidget;
|
||||||
|
VideoWidget* _videoWidget;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+1
-41
@@ -22,6 +22,7 @@
|
|||||||
#include <Wt/WNavigationBar>
|
#include <Wt/WNavigationBar>
|
||||||
#include <Wt/WPopupMenu>
|
#include <Wt/WPopupMenu>
|
||||||
#include <Wt/WPopupMenuItem>
|
#include <Wt/WPopupMenuItem>
|
||||||
|
#include <Wt/WVBoxLayout>
|
||||||
#include <Wt/Auth/Identity>
|
#include <Wt/Auth/Identity>
|
||||||
|
|
||||||
#include "settings/Settings.hpp"
|
#include "settings/Settings.hpp"
|
||||||
@@ -36,48 +37,7 @@ LmsHome::LmsHome(SessionData& sessionData, Wt::WContainerWidget* parent)
|
|||||||
{
|
{
|
||||||
const Wt::Auth::User& user = sessionData.getDatabaseHandler().getLogin().user();
|
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/>.
|
* 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 "logger/Logger.hpp"
|
||||||
|
|
||||||
|
#include "TableFilter.hpp"
|
||||||
|
#include "KeywordSearchFilter.hpp"
|
||||||
|
#include "TrackView.hpp"
|
||||||
|
|
||||||
#include "AudioWidget.hpp"
|
#include "AudioWidget.hpp"
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
@@ -28,33 +38,80 @@ namespace UserInterface {
|
|||||||
AudioWidget::AudioWidget(SessionData& sessionData, Wt::WContainerWidget* parent)
|
AudioWidget::AudioWidget(SessionData& sessionData, Wt::WContainerWidget* parent)
|
||||||
: Wt::WContainerWidget(parent),
|
: Wt::WContainerWidget(parent),
|
||||||
_db(sessionData.getDatabaseHandler()),
|
_db(sessionData.getDatabaseHandler()),
|
||||||
_audioDbWidget(nullptr),
|
_mediaPlayer(nullptr)
|
||||||
_mediaPlayer(nullptr),
|
|
||||||
_imgResource(nullptr),
|
|
||||||
_img(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);
|
_mediaPlayer->playbackEnded().connect(this, &AudioWidget::handleTrackEnded);
|
||||||
this->addWidget(new Wt::WBreak());
|
trackView->trackSelected().connect(boost::bind(&AudioWidget::playTrack, this, _1));
|
||||||
|
|
||||||
// Image
|
|
||||||
_imgResource = new Wt::WMemoryResource(this);
|
|
||||||
_imgLink.setResource( _imgResource);
|
|
||||||
_img = new Wt::WImage(_imgLink, this);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
AudioWidget::search(const std::string& searchText)
|
AudioWidget::search(const std::string& searchText)
|
||||||
{
|
{
|
||||||
_audioDbWidget->search(searchText);
|
_filterChain.searchKeyword(searchText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
AudioWidget::playTrack(boost::filesystem::path p)
|
AudioWidget::playTrack(boost::filesystem::path p)
|
||||||
{
|
{
|
||||||
@@ -71,7 +128,7 @@ AudioWidget::playTrack(boost::filesystem::path p)
|
|||||||
bitrate = user->getAudioBitrate();
|
bitrate = user->getAudioBitrate();
|
||||||
else
|
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?
|
return; // TODO logout?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,27 +140,6 @@ AudioWidget::playTrack(boost::filesystem::path p)
|
|||||||
parameters.setBitrate(Transcode::Stream::Audio, bitrate);
|
parameters.setBitrate(Transcode::Stream::Audio, bitrate);
|
||||||
|
|
||||||
_mediaPlayer->load( parameters );
|
_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)
|
catch( std::exception &e)
|
||||||
{
|
{
|
||||||
@@ -111,11 +147,10 @@ AudioWidget::playTrack(boost::filesystem::path p)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
AudioWidget::handleTrackEnded(void)
|
AudioWidget::handleTrackEnded(void)
|
||||||
{
|
{
|
||||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "Track playback ended!";
|
LMS_LOG(MOD_UI, SEV_DEBUG) << "Track playback ended!";
|
||||||
_audioDbWidget->selectNextTrack();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|||||||
@@ -22,13 +22,13 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <Wt/WLink>
|
|
||||||
#include <Wt/WImage>
|
|
||||||
#include <Wt/WContainerWidget>
|
#include <Wt/WContainerWidget>
|
||||||
#include <Wt/WMemoryResource>
|
|
||||||
|
|
||||||
#include "audio/AudioMediaPlayerWidget.hpp"
|
#include "common/SessionData.hpp"
|
||||||
#include "audio/AudioDatabaseWidget.hpp"
|
|
||||||
|
#include "AudioMediaPlayerWidget.hpp"
|
||||||
|
|
||||||
|
#include "FilterChain.hpp"
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
@@ -49,16 +49,9 @@ class AudioWidget : public Wt::WContainerWidget
|
|||||||
|
|
||||||
Database::Handler& _db;
|
Database::Handler& _db;
|
||||||
|
|
||||||
AudioDatabaseWidget* _audioDbWidget;
|
|
||||||
|
|
||||||
AudioMediaPlayerWidget* _mediaPlayer;
|
AudioMediaPlayerWidget* _mediaPlayer;
|
||||||
|
|
||||||
// Image
|
FilterChain _filterChain;
|
||||||
Wt::WMemoryResource *_imgResource;
|
|
||||||
Wt::WLink _imgLink;
|
|
||||||
Wt::WImage *_img;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -17,35 +17,30 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FILTER_WIDGET_HPP
|
#ifndef FILTER_HPP
|
||||||
#define FILTER_WIDGET_HPP
|
#define FILTER_HPP
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
|
||||||
#include <string>
|
|
||||||
#include <list>
|
|
||||||
|
|
||||||
#include <Wt/WSignal>
|
#include <Wt/WSignal>
|
||||||
#include <Wt/WContainerWidget>
|
|
||||||
|
|
||||||
#include "database/SqlQuery.hpp"
|
#include "database/SqlQuery.hpp"
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
class FilterWidget : public Wt::WContainerWidget {
|
class Filter
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
struct Constraint {
|
struct Constraint {
|
||||||
WhereClause where; // WHERE SQL clause
|
WhereClause where; // WHERE SQL clause
|
||||||
};
|
};
|
||||||
|
|
||||||
FilterWidget(Wt::WContainerWidget* parent = 0) : Wt::WContainerWidget(parent) {}
|
Filter() {}
|
||||||
virtual ~FilterWidget() {}
|
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;
|
virtual void refresh(const Constraint& constraint) = 0;
|
||||||
|
|
||||||
// Update constraints for child filters
|
// Update constraints for next filters
|
||||||
virtual void getConstraint(Constraint& constraint) = 0;
|
virtual void getConstraint(Constraint& constraint) = 0;
|
||||||
|
|
||||||
// Emitted when a constraint has changed
|
// 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/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Wt/WLabel>
|
#include "KeywordSearchFilter.hpp"
|
||||||
|
|
||||||
#include "SearchFilterWidget.hpp"
|
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
SearchFilterWidget::SearchFilterWidget(Wt::WContainerWidget* parent)
|
KeywordSearchFilter::KeywordSearchFilter()
|
||||||
: FilterWidget( parent )
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SearchFilterWidget::setText(const std::string& text)
|
KeywordSearchFilter::setText(const std::string& text)
|
||||||
{
|
{
|
||||||
_lastEmittedText = text;
|
_lastEmittedText = text;
|
||||||
emitUpdate();
|
emitUpdate();
|
||||||
@@ -37,7 +34,7 @@ SearchFilterWidget::setText(const std::string& text)
|
|||||||
|
|
||||||
// Get constraints created by this filter
|
// Get constraints created by this filter
|
||||||
void
|
void
|
||||||
SearchFilterWidget::getConstraint(Constraint& constraint)
|
KeywordSearchFilter::getConstraint(Constraint& constraint)
|
||||||
{
|
{
|
||||||
// No active search means no constaint!
|
// No active search means no constaint!
|
||||||
if (!_lastEmittedText.empty()) {
|
if (!_lastEmittedText.empty()) {
|
||||||
@@ -17,18 +17,19 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef SEARCH_FILTER_WIDGET_PP
|
#ifndef KEYWORD_SEARCH_FILTER_HPP
|
||||||
#define SEARCH_FILTER_WIDGET_PP
|
#define KEYWORD_SEARCH_FILTER_HPP
|
||||||
|
|
||||||
#include <Wt/WLineEdit>
|
#include <string>
|
||||||
|
|
||||||
#include "FilterWidget.hpp"
|
#include "Filter.hpp"
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
class SearchFilterWidget : public FilterWidget {
|
class KeywordSearchFilter : public Filter
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
SearchFilterWidget(Wt::WContainerWidget* parent = 0);
|
KeywordSearchFilter();
|
||||||
|
|
||||||
void setText(const std::string& text);
|
void setText(const std::string& text);
|
||||||
|
|
||||||
@@ -21,36 +21,49 @@
|
|||||||
|
|
||||||
#include "logger/Logger.hpp"
|
#include "logger/Logger.hpp"
|
||||||
|
|
||||||
#include "TableFilterWidget.hpp"
|
#include "TableFilter.hpp"
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
TableFilterWidget::TableFilterWidget(Database::Handler& db, std::string table, std::string field, Wt::WContainerWidget* parent)
|
TableFilter::TableFilter(Database::Handler& db, std::string table, std::string field, Wt::WContainerWidget* parent)
|
||||||
: FilterWidget( parent ),
|
: Wt::WTableView( parent ),
|
||||||
|
Filter(),
|
||||||
_db(db),
|
_db(db),
|
||||||
_table(table),
|
_table(table),
|
||||||
_field(field),
|
_field(field)
|
||||||
_tableView(nullptr)
|
|
||||||
{
|
{
|
||||||
_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.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( _table + "." + _field, table);
|
||||||
_queryModel.addColumn( "count(DISTINCT track.id)", "Tracks");
|
_queryModel.addColumn( "count(DISTINCT track.id)", "Tracks");
|
||||||
|
|
||||||
_tableView = new Wt::WTableView( this );
|
this->setSelectionMode(Wt::ExtendedSelection);
|
||||||
_tableView->resize(250, 200); // TODO
|
this->setSortingEnabled(false);
|
||||||
_tableView->setSelectionMode(Wt::ExtendedSelection);
|
this->setAlternatingRowColors(true);
|
||||||
_tableView->setSortingEnabled(false);
|
this->setModel(&_queryModel);
|
||||||
_tableView->setAlternatingRowColors(true);
|
|
||||||
_tableView->setModel(&_queryModel);
|
|
||||||
|
|
||||||
_tableView->selectionChanged().connect(this, &TableFilterWidget::emitUpdate);
|
this->selectionChanged().connect(this, &TableFilter::emitUpdate);
|
||||||
|
|
||||||
|
setLayoutSizeAware(true);
|
||||||
|
|
||||||
_queryModel.setBatchSize(100);
|
_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
|
// Set constraints on this filter
|
||||||
void
|
void
|
||||||
TableFilterWidget::refresh(const Constraint& constraint)
|
TableFilter::refresh(const Constraint& constraint)
|
||||||
{
|
{
|
||||||
SqlQuery sqlQuery;
|
SqlQuery sqlQuery;
|
||||||
|
|
||||||
@@ -73,16 +86,16 @@ TableFilterWidget::refresh(const Constraint& constraint)
|
|||||||
query.bind(bindArg);
|
query.bind(bindArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
_queryModel.setQuery( query, true /* Keep columns */);
|
_queryModel.setQuery( query, true );
|
||||||
|
|
||||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "Finish !";
|
LMS_LOG(MOD_UI, SEV_DEBUG) << "Finish !";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get constraint created by this filter
|
// Get constraint created by this filter
|
||||||
void
|
void
|
||||||
TableFilterWidget::getConstraint(Constraint& constraint)
|
TableFilter::getConstraint(Constraint& constraint)
|
||||||
{
|
{
|
||||||
Wt::WModelIndexSet indexSet = _tableView->selectedIndexes();
|
Wt::WModelIndexSet indexSet = this->selectedIndexes();
|
||||||
|
|
||||||
// WHERE statement
|
// WHERE statement
|
||||||
WhereClause clause;
|
WhereClause clause;
|
||||||
@@ -17,29 +17,31 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef TABLE_VIEW_FILTER_WIDGET_HPP
|
#ifndef TABLE_FILTER_HPP
|
||||||
#define TABLE_VIEW_FILTER_WIDGET_HPP
|
#define TABLE_FILTER_HPP
|
||||||
|
|
||||||
|
|
||||||
#include <Wt/Dbo/QueryModel>
|
#include <Wt/Dbo/QueryModel>
|
||||||
#include <Wt/WTableView>
|
#include <Wt/WTableView>
|
||||||
|
|
||||||
#include "FilterWidget.hpp"
|
#include "Filter.hpp"
|
||||||
#include "database/DatabaseHandler.hpp"
|
#include "database/DatabaseHandler.hpp"
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
class TableFilterWidget : public FilterWidget
|
class TableFilter : public Wt::WTableView, public Filter
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
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
|
// Set constraints on this filter
|
||||||
virtual void refresh(const Constraint& constraint);
|
void refresh(const Constraint& constraint);
|
||||||
|
|
||||||
// Get constraints created by this filter
|
// Get constraints created by this filter
|
||||||
virtual void getConstraint(Constraint& constraint);
|
void getConstraint(Constraint& constraint);
|
||||||
|
|
||||||
|
void layoutSizeChanged (int width, int height);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
@@ -51,7 +53,6 @@ class TableFilterWidget : public FilterWidget
|
|||||||
typedef boost::tuple<std::string, int, int> ResultType;
|
typedef boost::tuple<std::string, int, int> ResultType;
|
||||||
Wt::Dbo::QueryModel< ResultType > _queryModel;
|
Wt::Dbo::QueryModel< ResultType > _queryModel;
|
||||||
|
|
||||||
Wt::WTableView* _tableView;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
@@ -25,18 +25,14 @@
|
|||||||
|
|
||||||
#include "logger/Logger.hpp"
|
#include "logger/Logger.hpp"
|
||||||
|
|
||||||
#include "TrackWidget.hpp"
|
#include "TrackView.hpp"
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
TrackWidget::TrackWidget( Database::Handler& db, Wt::WContainerWidget* parent)
|
TrackView::TrackView( Database::Handler& db, Wt::WContainerWidget* parent)
|
||||||
: FilterWidget( parent ),
|
: Wt::WTableView( parent ),
|
||||||
_db(db),
|
_db(db)
|
||||||
_tableView(nullptr),
|
|
||||||
_trackStats(nullptr)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
// ----- 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.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( "artist.name", "Artist" );
|
||||||
_queryModel.addColumn( "release.name", "Album" );
|
_queryModel.addColumn( "release.name", "Album" );
|
||||||
@@ -48,56 +44,52 @@ _trackStats(nullptr)
|
|||||||
_queryModel.addColumn( "track.original_date", "Original Date" );
|
_queryModel.addColumn( "track.original_date", "Original Date" );
|
||||||
_queryModel.addColumn( "track.genre_list", "Genres" );
|
_queryModel.addColumn( "track.genre_list", "Genres" );
|
||||||
|
|
||||||
_queryModel.setBatchSize(1000);
|
_queryModel.setBatchSize(250);
|
||||||
|
|
||||||
_tableView = new Wt::WTableView( this );
|
this->setSortingEnabled(true);
|
||||||
_tableView->resize(Wt::WLength::Auto, 400);
|
this->setSelectionMode(Wt::SingleSelection);
|
||||||
|
this->setAlternatingRowColors(true);
|
||||||
|
this->setModel(&_queryModel);
|
||||||
|
|
||||||
_tableView->setSortingEnabled(true);
|
this->setColumnWidth(0, 180); // Artist
|
||||||
_tableView->setSelectionMode(Wt::SingleSelection);
|
this->setColumnWidth(1, 180); // Album
|
||||||
_tableView->setAlternatingRowColors(true);
|
this->setColumnWidth(2, 70); // Disc Number
|
||||||
_tableView->setModel(&_queryModel);
|
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
|
// Duration display
|
||||||
{
|
{
|
||||||
// TODO better handle 1 hour+ files!
|
// TODO better handle 1 hour+ files!
|
||||||
Wt::WItemDelegate *delegate = new Wt::WItemDelegate(this);
|
Wt::WItemDelegate *delegate = new Wt::WItemDelegate(this);
|
||||||
delegate->setTextFormat("mm:ss");
|
delegate->setTextFormat("mm:ss");
|
||||||
_tableView->setItemDelegateForColumn(5, delegate);
|
this->setItemDelegateForColumn(5, delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Date display, just the year
|
// Date display, just the year
|
||||||
{
|
{
|
||||||
Wt::WItemDelegate *delegate = new Wt::WItemDelegate(this);
|
Wt::WItemDelegate *delegate = new Wt::WItemDelegate(this);
|
||||||
delegate->setTextFormat("yyyy");
|
delegate->setTextFormat("yyyy");
|
||||||
_tableView->setItemDelegateForColumn(6, delegate);
|
this->setItemDelegateForColumn(6, delegate);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Wt::WItemDelegate *delegate = new Wt::WItemDelegate(this);
|
Wt::WItemDelegate *delegate = new Wt::WItemDelegate(this);
|
||||||
delegate->setTextFormat("yyyy");
|
delegate->setTextFormat("yyyy");
|
||||||
_tableView->setItemDelegateForColumn(7, delegate);
|
this->setItemDelegateForColumn(7, delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO other event!
|
// TODO other event!
|
||||||
_tableView->selectionChanged().connect(this, &TrackWidget::handleTrackSelected);
|
this->selectionChanged().connect(this, &TrackView::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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set constraints created by parent filters
|
// Set constraints created by parent filters
|
||||||
void
|
void
|
||||||
TrackWidget::refresh(const Constraint& constraint)
|
TrackView::refresh(const Constraint& constraint)
|
||||||
{
|
{
|
||||||
|
|
||||||
SqlQuery sqlQuery;
|
SqlQuery sqlQuery;
|
||||||
@@ -119,13 +111,12 @@ TrackWidget::refresh(const Constraint& constraint)
|
|||||||
|
|
||||||
_queryModel.setQuery( query, true );
|
_queryModel.setQuery( query, true );
|
||||||
|
|
||||||
updateStats();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TrackWidget::handleTrackSelected(void)
|
TrackView::handleTrackSelected(void)
|
||||||
{
|
{
|
||||||
Wt::WModelIndexSet indexSet = _tableView->selectedIndexes();
|
Wt::WModelIndexSet indexSet = this->selectedIndexes();
|
||||||
if (!indexSet.empty()) {
|
if (!indexSet.empty()) {
|
||||||
Wt::WModelIndex currentIndex( *indexSet.begin() );
|
Wt::WModelIndex currentIndex( *indexSet.begin() );
|
||||||
|
|
||||||
@@ -144,15 +135,15 @@ TrackWidget::handleTrackSelected(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TrackWidget::selectNextTrack(void)
|
TrackView::selectNextTrack(void)
|
||||||
{
|
{
|
||||||
Wt::WModelIndexSet indexSet = _tableView->selectedIndexes();
|
Wt::WModelIndexSet indexSet = this->selectedIndexes();
|
||||||
if (!indexSet.empty()) {
|
if (!indexSet.empty()) {
|
||||||
Wt::WModelIndex currentIndex( *indexSet.begin() );
|
Wt::WModelIndex currentIndex( *indexSet.begin() );
|
||||||
// Check there are remainin tracks!
|
// 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 );
|
ResultType result = _queryModel.resultRow( currentIndex.row() + 1 );
|
||||||
|
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef TRACK_WIDGET_HPP
|
#ifndef TRACK_HPP
|
||||||
#define TRACK_WIDGET_HPP
|
#define TRACK_HPP
|
||||||
|
|
||||||
#include <Wt/WTableView>
|
#include <Wt/WTableView>
|
||||||
#include <Wt/Dbo/QueryModel>
|
#include <Wt/Dbo/QueryModel>
|
||||||
@@ -26,15 +26,15 @@
|
|||||||
#include "database/DatabaseHandler.hpp"
|
#include "database/DatabaseHandler.hpp"
|
||||||
#include "database/AudioTypes.hpp"
|
#include "database/AudioTypes.hpp"
|
||||||
|
|
||||||
#include "FilterWidget.hpp"
|
#include "Filter.hpp"
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
class TrackWidget : public FilterWidget
|
class TrackView : public Wt::WTableView, public Filter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
TrackWidget( Database::Handler& db, Wt::WContainerWidget* parent = 0);
|
TrackView( Database::Handler& db, Wt::WContainerWidget* parent = 0);
|
||||||
|
|
||||||
// Set constraints created by parent filters
|
// Set constraints created by parent filters
|
||||||
void refresh(const Constraint& constraint);
|
void refresh(const Constraint& constraint);
|
||||||
@@ -59,10 +59,6 @@ class TrackWidget : public FilterWidget
|
|||||||
Wt::Dbo::QueryModel< ResultType > _queryModel;
|
Wt::Dbo::QueryModel< ResultType > _queryModel;
|
||||||
Wt::WTableView* _tableView;
|
Wt::WTableView* _tableView;
|
||||||
|
|
||||||
void updateStats(void);
|
|
||||||
|
|
||||||
Wt::WText* _trackStats;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
@@ -750,7 +750,7 @@ std::set<Test> tests = {
|
|||||||
Test::ReleaseFilterGenre,
|
Test::ReleaseFilterGenre,
|
||||||
// Test::TrackFilters,
|
// Test::TrackFilters,
|
||||||
// Test::Cover,
|
// Test::Cover,
|
||||||
// Test::Transcode,
|
Test::Transcode,
|
||||||
};
|
};
|
||||||
|
|
||||||
bool test(Test t)
|
bool test(Test t)
|
||||||
|
|||||||
Reference in New Issue
Block a user